@cmodjs/core 0.1.14 → 0.2.1

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.
Files changed (96) hide show
  1. package/docs/commands.md +32 -21
  2. package/docs/dependencies.md +8 -0
  3. package/docs/index.md +12 -4
  4. package/docs/install-steps.md +36 -4
  5. package/docs/jobs.md +25 -9
  6. package/docs/mod.md +103 -37
  7. package/docs/options.md +93 -0
  8. package/docs/permissions.md +88 -0
  9. package/docs/state.md +4 -31
  10. package/docs/testing.md +13 -2
  11. package/installer.d.ts +10 -0
  12. package/installer.d.ts.map +1 -0
  13. package/installer.js +8 -0
  14. package/jobs/context.d.ts +2 -2
  15. package/jobs/context.d.ts.map +1 -1
  16. package/jobs/context.js +5 -3
  17. package/jobs/permissions.js +1 -1
  18. package/jobs/program.d.ts.map +1 -1
  19. package/jobs/program.js +5 -1
  20. package/jobs/prompt.js +5 -5
  21. package/jobs/schedule.d.ts.map +1 -1
  22. package/jobs/schedule.js +2 -1
  23. package/jobs/slash-command.d.ts.map +1 -1
  24. package/jobs/slash-command.js +3 -2
  25. package/jobs/status-line.d.ts.map +1 -1
  26. package/jobs/status-line.js +3 -2
  27. package/jobs/tool.d.ts.map +1 -1
  28. package/jobs/tool.js +3 -2
  29. package/mod.d.ts +48 -10
  30. package/mod.d.ts.map +1 -1
  31. package/mod.js +3 -0
  32. package/options.d.ts +58 -0
  33. package/options.d.ts.map +1 -0
  34. package/options.js +66 -0
  35. package/package.json +1 -1
  36. package/records.d.ts +16 -1
  37. package/records.d.ts.map +1 -1
  38. package/records.js +119 -7
  39. package/register.d.ts +4 -2
  40. package/register.d.ts.map +1 -1
  41. package/register.js +8 -2
  42. package/runtime/claude.d.ts +5 -1
  43. package/runtime/claude.d.ts.map +1 -1
  44. package/runtime/grants.d.ts +28 -0
  45. package/runtime/grants.d.ts.map +1 -0
  46. package/runtime/grants.js +286 -0
  47. package/runtime/installer.d.ts +24 -0
  48. package/runtime/installer.d.ts.map +1 -0
  49. package/runtime/installer.js +118 -0
  50. package/runtime/lifecycle.d.ts +6 -3
  51. package/runtime/lifecycle.d.ts.map +1 -1
  52. package/runtime/lifecycle.js +226 -36
  53. package/runtime/metadata.d.ts +20 -0
  54. package/runtime/metadata.d.ts.map +1 -0
  55. package/runtime/metadata.js +143 -0
  56. package/runtime/options.d.ts +21 -0
  57. package/runtime/options.d.ts.map +1 -0
  58. package/runtime/options.js +100 -0
  59. package/runtime/state.d.ts +1 -0
  60. package/runtime/state.d.ts.map +1 -1
  61. package/runtime/state.js +5 -75
  62. package/src/installer.ts +17 -0
  63. package/src/jobs/context.ts +5 -3
  64. package/src/jobs/permissions.ts +1 -1
  65. package/src/jobs/program.ts +4 -1
  66. package/src/jobs/prompt.ts +5 -5
  67. package/src/jobs/schedule.ts +2 -1
  68. package/src/jobs/slash-command.ts +3 -2
  69. package/src/jobs/status-line.ts +3 -2
  70. package/src/jobs/tool.ts +3 -2
  71. package/src/mod.ts +46 -11
  72. package/src/options.ts +91 -0
  73. package/src/records.ts +131 -8
  74. package/src/register.ts +12 -3
  75. package/src/runtime/claude.ts +2 -0
  76. package/src/runtime/grants.ts +306 -0
  77. package/src/runtime/installer.ts +169 -0
  78. package/src/runtime/lifecycle.ts +227 -45
  79. package/src/runtime/metadata.ts +153 -0
  80. package/src/runtime/options.ts +102 -0
  81. package/src/runtime/state.ts +7 -70
  82. package/src/testing/fake-claude.ts +6 -1
  83. package/src/testing/fake-files.ts +9 -3
  84. package/src/testing.ts +40 -15
  85. package/src/utils/metadata.ts +242 -0
  86. package/testing/fake-claude.d.ts +4 -0
  87. package/testing/fake-claude.d.ts.map +1 -1
  88. package/testing/fake-claude.js +5 -1
  89. package/testing/fake-files.d.ts.map +1 -1
  90. package/testing/fake-files.js +9 -3
  91. package/testing.d.ts +6 -2
  92. package/testing.d.ts.map +1 -1
  93. package/testing.js +29 -13
  94. package/utils/metadata.d.ts +21 -0
  95. package/utils/metadata.d.ts.map +1 -0
  96. package/utils/metadata.js +250 -0
package/records.js CHANGED
@@ -1,4 +1,9 @@
1
1
  import { listed, messageOf } from './utils/text.js';
2
+ export const listPermissions = ['network', 'run', 'files'];
3
+ export const flagPermissions = ['conversation', 'prompt', 'model', 'agents', 'tools', 'config', 'approve'];
4
+ const permissionNames = [...listPermissions, ...flagPermissions];
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
+ const programName = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
2
7
  export const pluginName = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
3
8
  export function storeFolder(env) {
4
9
  const dataHome = env['XDG_DATA_HOME'];
@@ -94,7 +99,104 @@ export function readSteps(pkg) {
94
99
  const program = steps['program'];
95
100
  if (program !== undefined && (typeof program !== 'string' || !pluginName.test(program)))
96
101
  throw new Error('package.json "cmod.program" must be the program\'s command, such as "hello".');
97
- return { ...readCommand(steps, 'install'), ...readCommand(steps, 'uninstall'), ...(program === undefined ? {} : { program }), ...readKeys(steps['keys']) };
102
+ const permissions = readPermissions(steps);
103
+ return {
104
+ ...readCommand(steps, 'install'),
105
+ ...readCommand(steps, 'uninstall'),
106
+ ...(program === undefined ? {} : { program }),
107
+ ...readKeys(steps['keys']),
108
+ ...(permissions.length === 0 ? {} : { permissions }),
109
+ };
110
+ }
111
+ function readPermissions(steps) {
112
+ const declared = steps['permissions'];
113
+ if (declared === undefined)
114
+ return [];
115
+ const at = 'package.json "cmod.permissions"';
116
+ if (!isObject(declared))
117
+ throw new Error(`${at} must be an object, such as { "network": ["api.github.com"], "prompt": true }.`);
118
+ const items = [];
119
+ for (const [name, value] of Object.entries(declared)) {
120
+ if (!permissionNames.includes(name))
121
+ throw new Error(`${at} names "${name}", which is not a permission. Use ${listed(permissionNames.map((each) => `"${each}"`))}.`);
122
+ if (flagPermissions.includes(name)) {
123
+ if (value !== true)
124
+ throw new Error(`${at} sets "${name}" to ${JSON.stringify(value)}. Write "${name}": true, or leave it out.`);
125
+ items.push(name);
126
+ continue;
127
+ }
128
+ if (name === 'run' && value === '*') {
129
+ items.push('run:*');
130
+ continue;
131
+ }
132
+ const example = name === 'network' ? '["api.github.com"]' : name === 'run' ? '["gh"], or "*" for any program' : '["~/.zshrc"]';
133
+ if (!Array.isArray(value) || value.length === 0 || !value.every((each) => typeof each === 'string'))
134
+ throw new Error(`${at} sets "${name}" to ${JSON.stringify(value)}. Write a list, such as "${name}": ${example}.`);
135
+ for (const each of value) {
136
+ const isPath = /^(~\/|\/)/.test(each) && !each.split('/').includes('..');
137
+ const isValid = name === 'network' ? hostName.test(each) || isPath : name === 'run' ? programName.test(each) : isPath;
138
+ if (!isValid)
139
+ throw new Error(`${at} lists "${each}" under "${name}". Write ${name === 'network' ? 'a host alone, such as "api.github.com", or a socket path, such as "/var/run/docker.sock"' : name === 'run' ? 'a program\'s command, such as "gh"' : 'a path that starts with ~/ or /, such as "~/.zshrc"'}.`);
140
+ items.push(`${name}:${each}`);
141
+ }
142
+ }
143
+ return items;
144
+ }
145
+ export function permissionWords(item) {
146
+ const split = item.indexOf(':');
147
+ const target = item.slice(split + 1);
148
+ switch (split === -1 ? item : item.slice(0, split)) {
149
+ case 'network':
150
+ return `Connect to ${target}`;
151
+ case 'run':
152
+ return target === '*' ? 'Run any program on your computer' : `Run ${target} on your computer`;
153
+ case 'files':
154
+ return `Change ${target}`;
155
+ case 'conversation':
156
+ return 'Read this conversation';
157
+ case 'prompt':
158
+ return 'Add text Claude reads and start turns';
159
+ case 'model':
160
+ return 'Ask a model, which uses your plan';
161
+ case 'agents':
162
+ return 'Start agents';
163
+ case 'tools':
164
+ return "Use and change Claude's tool calls";
165
+ case 'config':
166
+ return 'Change your Claude Code settings';
167
+ case 'approve':
168
+ return "Approve Claude's tool calls for you";
169
+ default:
170
+ return item;
171
+ }
172
+ }
173
+ export const settingsPagesMethod = 'cmod:settingsPages';
174
+ export const openPageMethod = 'cmod:openPage';
175
+ export const pendingStepsMethod = 'cmod:pendingSteps';
176
+ export const finishStepsMethod = 'cmod:finishSteps';
177
+ export function marketplaceOf(root, name) {
178
+ const installed = /\/cache\/([^/]+)\/([^/]+)\/[^/]+\/?$/.exec(root);
179
+ return installed?.[2] === name ? installed[1] : undefined;
180
+ }
181
+ export function updatesToTurnOn(marketplace, settings, known) {
182
+ if (marketplace === undefined)
183
+ return undefined;
184
+ const declared = isObject(settings) && isObject(settings['extraKnownMarketplaces']) ? settings['extraKnownMarketplaces'][marketplace] : undefined;
185
+ const fetched = isObject(known) ? known[marketplace] : undefined;
186
+ const isDecided = [declared, fetched].some((entry) => isObject(entry) && typeof entry['autoUpdate'] === 'boolean');
187
+ const hasSource = [declared, fetched].some((entry) => isObject(entry) && isObject(entry['source']));
188
+ return isDecided || !hasSource ? undefined : marketplace;
189
+ }
190
+ export function consentPath(store) {
191
+ return `${store}/consent.json`;
192
+ }
193
+ export function parseConsent(value, path) {
194
+ if (value === undefined)
195
+ return Object.create(null);
196
+ if (!isObject(value) || Object.values(value).some((items) => !Array.isArray(items) || items.some((item) => typeof item !== 'string'))) {
197
+ throw new Error(`${path} is not a map of plugin names to what each was approved for. Delete it, and cmod asks again.`);
198
+ }
199
+ return Object.assign(Object.create(null), value);
98
200
  }
99
201
  function readKeys(keys) {
100
202
  if (keys === undefined)
@@ -110,8 +212,10 @@ function readKeys(keys) {
110
212
  }
111
213
  return { keys: commands };
112
214
  }
113
- export function oldestCmodFor(keys) {
114
- return Object.keys(keys).length > 0 ? '0.1.12' : '0.0.0';
215
+ export function oldestCmodFor(steps) {
216
+ if (steps.permissions !== undefined)
217
+ return '0.2.0';
218
+ return Object.keys(steps.keys ?? {}).length > 0 ? '0.1.12' : '0.0.0';
115
219
  }
116
220
  export function keyWords(keys) {
117
221
  return listed(Object.entries(keys ?? {}).map(([key, command]) => `${key} to /${command}`));
@@ -155,7 +259,13 @@ export async function scriptsSha256(steps, files) {
155
259
  for (const folder of scriptFolders)
156
260
  folders.add(folder);
157
261
  }
158
- let text = [steps.install ?? '', steps.uninstall ?? '', steps.program ?? '', ...(steps.keys === undefined ? [] : [JSON.stringify(Object.entries(steps.keys).sort())])].join('\0');
262
+ let text = [
263
+ steps.install ?? '',
264
+ steps.uninstall ?? '',
265
+ steps.program ?? '',
266
+ ...(steps.keys === undefined ? [] : [JSON.stringify(Object.entries(steps.keys).sort())]),
267
+ ...(steps.permissions === undefined ? [] : [JSON.stringify([...steps.permissions].sort())]),
268
+ ].join('\0');
159
269
  for (const folder of [...folders].sort()) {
160
270
  for (const name of (await files.list(folder)).sort()) {
161
271
  const path = `${folder}/${name}`;
@@ -175,7 +285,7 @@ export function formatEvent(event) {
175
285
  case 'log':
176
286
  return `log ${event.text}`;
177
287
  case 'needs-consent':
178
- return `needs-consent ${event.sha256}\t${event.install}\t${event.uninstall}\t${event.keys}`;
288
+ return `needs-consent ${[event.sha256, event.install, event.uninstall, event.keys, ...event.permissions].join('\t')}`;
179
289
  case 'done':
180
290
  return event.version === undefined ? `done ${event.name}` : `done ${event.name} ${event.version}`;
181
291
  case 'missing':
@@ -189,8 +299,10 @@ export function parseEvent(line) {
189
299
  if (progress !== null)
190
300
  return { kind: 'progress', done: Number(progress[1]), total: Number(progress[2]), label: progress[3] ?? '' };
191
301
  const consent = /^needs-consent (\S+)\t([^\t]*)\t([^\t]*)(?:\t(.*))?$/.exec(line);
192
- if (consent !== null)
193
- return { kind: 'needs-consent', sha256: consent[1], install: consent[2], uninstall: consent[3], keys: consent[4] ?? '' };
302
+ if (consent !== null) {
303
+ const [keys = '', ...permissions] = (consent[4] ?? '').split('\t');
304
+ return { kind: 'needs-consent', sha256: consent[1], install: consent[2], uninstall: consent[3], keys, permissions: permissions.filter((item) => item !== '') };
305
+ }
194
306
  const done = /^done (\S+)(?: (\S+))?$/.exec(line);
195
307
  if (done !== null)
196
308
  return { kind: 'done', name: done[1], ...(done[2] === undefined ? {} : { version: done[2] }) };
package/register.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import type { On } from 'claude-code';
1
+ import type { On, PluginOptions } from 'claude-code';
2
2
  import type { ModDefinition } from './mod.js';
3
- export declare function registerMod<State extends object>(addHook: On, definition: ModDefinition<State>): void;
3
+ import type { Options } from './options.js';
4
+ export declare function registeredMod(): ModDefinition<object> | undefined;
5
+ export declare function registerMod<State extends object, Declared extends Options>(addHook: On, definition: ModDefinition<State, string, Declared>, options: PluginOptions): void;
4
6
  export declare function registerPermissionCheck(addHook: On): void;
5
7
  //# sourceMappingURL=register.d.ts.map
package/register.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoD,EAAE,EAAgB,MAAM,aAAa,CAAA;AACrG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAkF7C,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CA8BrG;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAIzD"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoD,EAAE,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAA;AACpH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAW3C,wBAAgB,aAAa,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAEjE;AA4ED,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,SAAS,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CA+BzK;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAIzD"}
package/register.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { createLifecycle, readPlugin } from './runtime/lifecycle.js';
2
2
  let lifecycle;
3
3
  let checksPermissions = false;
4
+ let registered;
5
+ export function registeredMod() {
6
+ return registered;
7
+ }
4
8
  async function startMod($, eventInput, passOn) {
5
9
  const claude = {
6
10
  plugin: { name: $.plugin.name, root: $.plugin.root },
@@ -30,6 +34,7 @@ async function startMod($, eventInput, passOn) {
30
34
  },
31
35
  http: { fetch: (url, init) => $.http.fetch(url, init) },
32
36
  settings: { read: (args) => $.settings.read(args) },
37
+ config: { list: () => $.config.list(), set: (args) => $.config.set(args) },
33
38
  store: {
34
39
  get: (key) => $.store.get(key),
35
40
  set: (key, value) => $.store.set(key, value),
@@ -72,9 +77,10 @@ async function startMod($, eventInput, passOn) {
72
77
  function routeToMod(_$, eventInput, passOn) {
73
78
  return lifecycle.route(passOn.event, eventInput, (passed) => passOn(passed));
74
79
  }
75
- export function registerMod(addHook, definition) {
80
+ export function registerMod(addHook, definition, options) {
76
81
  checksPermissions = false;
77
- lifecycle = createLifecycle(definition, () => checksPermissions);
82
+ registered = definition;
83
+ lifecycle = createLifecycle(definition, () => checksPermissions, options);
78
84
  addHook('session.start', startMod);
79
85
  addHook('classic.SessionStart', routeToMod);
80
86
  addHook('classic.SessionEnd', routeToMod);
@@ -1,4 +1,4 @@
1
- import type { AgentInfo, AgentSpawnArgs, AgentSpawnResult, Args, EngineInterface, AskOptions, CommandSpec, ElementTable, FsEntry, FsStat, FsStatOptions, HookStream, HttpInit, HttpResponse, InvalidatableEventName, PaneCloseArgs, PaneOpenArgs, ProcessRunInit, ProcessRunResult, ProcessSpawnChunk, ProcessSpawnRequest, ProcessSpawnResult, RenderSurface, ResolveInput, SessionUsage, Settings, SettingsReadArgs, Timer, ToastOptions, ToolSpec, UiLogOptions, UiOpenResult, UiPane, UiScrollArgs, UiScrollResult } from 'claude-code';
1
+ import type { AgentInfo, AgentSpawnArgs, AgentSpawnResult, Args, EngineInterface, AskOptions, CommandSpec, ConfigRow, ElementTable, FsEntry, FsStat, FsStatOptions, HookStream, HttpInit, HttpResponse, InvalidatableEventName, PaneCloseArgs, PaneOpenArgs, ProcessRunInit, ProcessRunResult, ProcessSpawnChunk, ProcessSpawnRequest, ProcessSpawnResult, RenderSurface, ResolveInput, SessionUsage, Settings, SettingsReadArgs, Timer, ToastOptions, ToolSpec, UiLogOptions, UiOpenResult, UiPane, UiScrollArgs, UiScrollResult } from 'claude-code';
2
2
  export type Claude = {
3
3
  readonly plugin: {
4
4
  readonly name: string;
@@ -34,6 +34,10 @@ export type Claude = {
34
34
  readonly settings: {
35
35
  read(args?: SettingsReadArgs): Promise<Settings>;
36
36
  };
37
+ readonly config: {
38
+ list(): Promise<ConfigRow[]>;
39
+ set: EngineInterface['config']['set'];
40
+ };
37
41
  readonly store: {
38
42
  get(key: string): Promise<unknown>;
39
43
  set(key: string, value: unknown): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/runtime/claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/runtime/claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,eAAe,EACf,UAAU,EACV,WAAW,EACX,SAAS,EACT,YAAY,EACZ,OAAO,EACP,MAAM,EACN,aAAa,EACb,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,YAAY,EACZ,cAAc,EACf,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACjE,QAAQ,CAAC,EAAE,EAAE;QACX,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;QACjD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;QACtC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;QAC/C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;QACzD,UAAU,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAA;QAC/C,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;QAChF,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;QAC/C,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QACzC,KAAK,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;QACnC,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;QACnD,OAAO,CAAC,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,aAAa,CAAC,CAAA;KACtD,CAAA;IACD,QAAQ,CAAC,OAAO,EAAE;QAChB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC9E,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAA;KACvF,CAAA;IACD,QAAQ,CAAC,EAAE,EAAE;QACX,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;QACnC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;QACvC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;KAC7D,CAAA;IACD,QAAQ,CAAC,IAAI,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;KAAE,CAAA;IAC7E,QAAQ,CAAC,QAAQ,EAAE;QAAE,IAAI,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IACvE,QAAQ,CAAC,MAAM,EAAE;QAAE,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QAAC,GAAG,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAA;KAAE,CAAA;IACxF,QAAQ,CAAC,KAAK,EAAE;QACd,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;QAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;KAC1B,CAAA;IACD,QAAQ,CAAC,KAAK,EAAE;QACd,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;QACtB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAAA;QACxC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAAA;KACzC,CAAA;IACD,QAAQ,CAAC,OAAO,EAAE;QAChB,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;QACrB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;QACvB,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;QACtB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;QACxB,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,CAAA;QAC9B,QAAQ,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAA;QAC7C,QAAQ,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,CAAA;QAChD,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAA;KAC7C,CAAA;IACD,QAAQ,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAA;IAChE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAA;KAAE,CAAA;IAClE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAA;IAClF,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IAC7G,QAAQ,CAAC,KAAK,EAAE;QACd,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;QAC5B,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;KACvD,CAAA;IACD,QAAQ,CAAC,GAAG,EAAE;QACZ,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;QACnC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;QACvC,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;KAC1C,CAAA;IACD,QAAQ,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;CACpE,CAAA"}
@@ -0,0 +1,28 @@
1
+ import type { Claude } from './claude.js';
2
+ import type { RoutedEvent } from './hooks.js';
3
+ export type Grants = {
4
+ readonly name: string;
5
+ readonly declared: readonly string[];
6
+ readonly granted: () => ReadonlySet<string>;
7
+ readonly refresh: () => Promise<void>;
8
+ readonly home: string | undefined;
9
+ readonly configRoot: string | undefined;
10
+ readonly projectRoot: () => string;
11
+ readonly freeFolders: () => readonly string[];
12
+ };
13
+ export declare class PermissionRefused extends Error {
14
+ }
15
+ export declare function checkWrite(grants: Grants, call: string, path: string): Promise<void>;
16
+ export declare function checkingGrants(claude: Claude, grants: Grants): Claude;
17
+ export declare function itemOf(name: string, value?: string): string;
18
+ export declare function isCovered(granted: ReadonlySet<string>, item: string, home: string | undefined): boolean;
19
+ export declare function refusal(grants: Pick<Grants, 'name' | 'declared' | 'home'>, call: string, item: string): string;
20
+ export type AnswerCheck = {
21
+ readonly plugin: string;
22
+ isGranted(item: string): boolean;
23
+ dropped(item: string, part: string): void;
24
+ };
25
+ export declare function checksAnswers(event: RoutedEvent): boolean;
26
+ export declare function passedDown(event: RoutedEvent, original: unknown, passed: unknown, check: AnswerCheck): unknown;
27
+ export declare function checkedAnswer(event: RoutedEvent, e: unknown, answer: unknown, below: () => Promise<unknown>, check: AnswerCheck): Promise<unknown>;
28
+ //# sourceMappingURL=grants.d.ts.map
@@ -0,0 +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;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,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;CAC9C,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAc/C,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA6ErE;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,CAUvG;AAcD,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"}
@@ -0,0 +1,286 @@
1
+ import { permissionWords } from '../records.js';
2
+ import { relativePath } from '../utils/paths.js';
3
+ export class PermissionRefused extends Error {
4
+ }
5
+ const configFiles = ['.claude/settings.json', '.claude/settings.local.json', '.mcp.json'];
6
+ const startsTurns = ['CronCreate'];
7
+ const ownSchedule = ['CronDelete', 'CronList'];
8
+ async function checkGrant(grants, call, item) {
9
+ if (item === undefined)
10
+ return;
11
+ await grants.refresh();
12
+ if (!isCovered(grants.granted(), item, grants.home))
13
+ throw new PermissionRefused(refusal(grants, call, item));
14
+ }
15
+ export function checkWrite(grants, call, path) {
16
+ return checkGrant(grants, call, writeItem(grants, path));
17
+ }
18
+ export function checkingGrants(claude, grants) {
19
+ const gate = (call, item) => checkGrant(grants, call, item);
20
+ return {
21
+ ...claude,
22
+ process: {
23
+ async run(argv, init) {
24
+ await gate(`process.run(${argv.join(' ')})`, `run:${programOf(argv)}`);
25
+ return claude.process.run(argv, init);
26
+ },
27
+ spawn(request) {
28
+ const started = gate(`process.spawn(${request.argv.join(' ')})`, `run:${programOf(request.argv)}`).then(() => claude.process.spawn(request));
29
+ return spawned(started);
30
+ },
31
+ },
32
+ fs: {
33
+ ...claude.fs,
34
+ async read(path) {
35
+ await gate(`fs.read(${path})`, readItem(grants, path));
36
+ return claude.fs.read(path);
37
+ },
38
+ async write(path, text) {
39
+ await gate(`fs.write(${path})`, writeItem(grants, path));
40
+ return claude.fs.write(path, text);
41
+ },
42
+ },
43
+ http: {
44
+ async fetch(url, init) {
45
+ await gate(`http.fetch(${url})`, `network:${init?.socketPath ?? hostOf(url)}`);
46
+ return claude.http.fetch(url, init);
47
+ },
48
+ },
49
+ config: {
50
+ ...claude.config,
51
+ set: (async (args) => {
52
+ await gate('config.set', 'config');
53
+ return claude.config.set(args);
54
+ }),
55
+ },
56
+ session: {
57
+ ...claude.session,
58
+ messages: (async (...args) => {
59
+ await gate('session.messages', 'conversation');
60
+ return claude.session.messages(...args);
61
+ }),
62
+ append: (async (args) => {
63
+ await gate('session.append', 'prompt');
64
+ return claude.session.append(args);
65
+ }),
66
+ },
67
+ prompt: {
68
+ submit: (async (args) => {
69
+ await gate('session.submit', 'prompt');
70
+ return claude.prompt.submit(args);
71
+ }),
72
+ },
73
+ model: {
74
+ complete: (async (...args) => {
75
+ await gate('model.complete', 'model');
76
+ return claude.model.complete(...args);
77
+ }),
78
+ },
79
+ agent: {
80
+ ...claude.agent,
81
+ async spawn(args) {
82
+ await gate('agent.spawn', 'agents');
83
+ return claude.agent.spawn(args);
84
+ },
85
+ },
86
+ tool: {
87
+ ...claude.tool,
88
+ call: (async (input) => {
89
+ const item = startsTurns.includes(input.tool) ? 'prompt' : ownSchedule.includes(input.tool) ? undefined : 'tools';
90
+ await gate(`tool.call(${input.tool})`, item);
91
+ return claude.tool.call(input);
92
+ }),
93
+ },
94
+ };
95
+ }
96
+ function spawned(started) {
97
+ const result = started.then((stream) => stream.result);
98
+ result.catch(() => undefined);
99
+ const stream = {
100
+ next: async () => (await started).next(),
101
+ async return(value) {
102
+ const running = await started.catch(() => undefined);
103
+ return (await running?.return?.(value)) ?? { done: true, value };
104
+ },
105
+ [Symbol.asyncIterator]: () => stream,
106
+ result,
107
+ };
108
+ return stream;
109
+ }
110
+ export function itemOf(name, value) {
111
+ return value === undefined ? name : `${name}:${value}`;
112
+ }
113
+ export function isCovered(granted, item, home) {
114
+ if (granted.has(item))
115
+ return true;
116
+ const split = item.indexOf(':');
117
+ if (split === -1)
118
+ return false;
119
+ const name = item.slice(0, split);
120
+ const target = expanded(item.slice(split + 1), home);
121
+ const values = [...granted].filter((each) => each.startsWith(`${name}:`)).map((each) => expanded(each.slice(name.length + 1), home));
122
+ if (name === 'run')
123
+ return values.includes('*') || values.includes(target);
124
+ if (name === 'files')
125
+ return values.some((each) => relativePath(each, target) !== undefined);
126
+ return values.includes(target);
127
+ }
128
+ function readItem(grants, path) {
129
+ if (grants.configRoot === undefined)
130
+ return undefined;
131
+ return relativePath(`${grants.configRoot}/projects`, path) === undefined ? undefined : 'conversation';
132
+ }
133
+ function writeItem(grants, path) {
134
+ const root = grants.projectRoot();
135
+ if (configFiles.some((file) => `${root}/${file}` === path))
136
+ return 'config';
137
+ if (grants.freeFolders().some((folder) => relativePath(folder, path) !== undefined))
138
+ return undefined;
139
+ return `files:${path}`;
140
+ }
141
+ export function refusal(grants, call, item) {
142
+ const { name } = grants;
143
+ if (isCovered(new Set(grants.declared), item, grants.home))
144
+ return `${name} calls ${call} without your grant to "${permissionWords(item)}". Turn it on in /mods ${name}.`;
145
+ return `${name} calls ${call}, which needs ${declaration(item, grants.home)} in package.json "cmod".`;
146
+ }
147
+ function declaration(item, home) {
148
+ const split = item.indexOf(':');
149
+ if (split === -1)
150
+ return `"permissions": { "${item}": true }`;
151
+ const name = item.slice(0, split);
152
+ const target = item.slice(split + 1);
153
+ const fromHome = home === undefined ? undefined : relativePath(home, target);
154
+ const shown = name !== 'run' && fromHome !== undefined ? `~/${fromHome}` : target;
155
+ return `"permissions": { "${name}": ["${shown}"] }`;
156
+ }
157
+ const classicParts = {
158
+ additionalContext: 'prompt',
159
+ initialUserMessage: 'prompt',
160
+ suppressOriginalPrompt: 'prompt',
161
+ updatedToolOutput: 'prompt',
162
+ updatedMCPToolOutput: 'prompt',
163
+ updatedInput: 'tools',
164
+ retry: 'approve',
165
+ };
166
+ const blocksKeepWorking = ['classic.Stop', 'classic.PostToolUse'];
167
+ const reservedToolKeys = ['tool', 'tool_use_id'];
168
+ export function checksAnswers(event) {
169
+ return event.startsWith('classic.') || event === 'tool.check' || event === 'tool.call' || event === 'prompt.context' || event === 'prompt.submit';
170
+ }
171
+ export function passedDown(event, original, passed, check) {
172
+ if (passed === original)
173
+ return passed;
174
+ if (event === 'tool.call') {
175
+ if (sameExcept(original, passed, reservedToolKeys) || check.isGranted('tools'))
176
+ return passed;
177
+ check.dropped('tools', 'a changed tool input');
178
+ return original;
179
+ }
180
+ if (event === 'prompt.context' || event === 'prompt.submit') {
181
+ if (same(original, passed) || check.isGranted('prompt'))
182
+ return passed;
183
+ check.dropped('prompt', event === 'prompt.submit' ? 'a rewritten prompt' : 'changed context');
184
+ return original;
185
+ }
186
+ return passed;
187
+ }
188
+ export async function checkedAnswer(event, e, answer, below, check) {
189
+ if (!isFields(answer))
190
+ return answer;
191
+ if (event === 'tool.check')
192
+ return decided(answer['decision'], answer, below, check);
193
+ if (event === 'classic.PermissionRequest') {
194
+ const decision = isFields(answer['decision']) ? answer['decision']['behavior'] : undefined;
195
+ return decided(decision, answer, below, check);
196
+ }
197
+ if (event.startsWith('classic.'))
198
+ return classicAnswer(event, answer, below, check);
199
+ if (event === 'tool.call') {
200
+ if (answer['deny'] !== undefined || check.isGranted('prompt'))
201
+ return answer;
202
+ const tool = isFields(e) ? e['tool'] : undefined;
203
+ if (typeof tool === 'string' && tool.startsWith(`mcp__${check.plugin}__`)) {
204
+ if (answer['context'] === undefined)
205
+ return answer;
206
+ check.dropped('prompt', 'context after a tool call');
207
+ const { context: _context, ...rest } = answer;
208
+ return rest;
209
+ }
210
+ const theirs = await below();
211
+ if (same(answer, theirs))
212
+ return answer;
213
+ check.dropped('prompt', same(contextOf(answer), contextOf(theirs)) ? 'a changed tool result' : 'context after a tool call');
214
+ return theirs;
215
+ }
216
+ if (event === 'prompt.submit' && answer['drop'] !== undefined)
217
+ return answer;
218
+ if (event === 'prompt.context' || event === 'prompt.submit') {
219
+ if (check.isGranted('prompt'))
220
+ return answer;
221
+ const theirs = await below();
222
+ if (same(answer, theirs))
223
+ return answer;
224
+ check.dropped('prompt', event === 'prompt.submit' ? 'a rewritten prompt' : 'changed context');
225
+ return theirs;
226
+ }
227
+ return answer;
228
+ }
229
+ async function decided(decision, answer, below, check) {
230
+ if ((decision !== 'allow' && decision !== 'ask') || check.isGranted('approve'))
231
+ return answer;
232
+ const theirs = await below();
233
+ if (same(answer, theirs))
234
+ return answer;
235
+ check.dropped('approve', `${decision} on a tool call`);
236
+ return theirs;
237
+ }
238
+ async function classicAnswer(event, answer, below, check) {
239
+ const blocks = blocksKeepWorking.includes(event) ? [['block', 'prompt']] : [];
240
+ const parts = [...Object.entries(classicParts), ...blocks].filter(([part, item]) => answer[part] !== undefined && !check.isGranted(item));
241
+ if (parts.length === 0)
242
+ return answer;
243
+ const theirs = await below();
244
+ const kept = { ...answer };
245
+ const theirFields = isFields(theirs) ? theirs : {};
246
+ for (const [part, item] of parts) {
247
+ if (same(answer[part], theirFields[part]))
248
+ continue;
249
+ check.dropped(item, part);
250
+ if (theirFields[part] === undefined)
251
+ delete kept[part];
252
+ else
253
+ kept[part] = theirFields[part];
254
+ }
255
+ return kept;
256
+ }
257
+ function contextOf(answer) {
258
+ return isFields(answer) ? answer['context'] : undefined;
259
+ }
260
+ function same(left, right) {
261
+ return left === right || JSON.stringify(left) === JSON.stringify(right);
262
+ }
263
+ function sameExcept(left, right, keys) {
264
+ if (!isFields(left) || !isFields(right))
265
+ return same(left, right);
266
+ const without = (fields) => Object.fromEntries(Object.entries(fields).filter(([key]) => !keys.includes(key)));
267
+ return same(without(left), without(right));
268
+ }
269
+ function isFields(value) {
270
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
271
+ }
272
+ function programOf(argv) {
273
+ const command = argv[0] ?? '';
274
+ return command.slice(command.lastIndexOf('/') + 1);
275
+ }
276
+ function hostOf(url) {
277
+ try {
278
+ return new URL(url).hostname;
279
+ }
280
+ catch {
281
+ return url;
282
+ }
283
+ }
284
+ function expanded(path, home) {
285
+ return path.startsWith('~/') && home !== undefined ? `${home}${path.slice(1)}` : path;
286
+ }
@@ -0,0 +1,24 @@
1
+ import type { Args, Frozen, RenderElement } from 'claude-code';
2
+ import type { Step } from '../installer.js';
3
+ import type { Mod } from '../mod.js';
4
+ import type { Option } from '../options.js';
5
+ import type { Claude } from './claude.js';
6
+ export declare const installerPaneId = "cmod-installer";
7
+ export type Consent = {
8
+ readonly install: string;
9
+ readonly uninstall: string;
10
+ readonly keys: string;
11
+ readonly permissions: readonly string[];
12
+ readonly updates?: string | undefined;
13
+ };
14
+ export type Installer = {
15
+ draws(e: Frozen<Args<'ui.render'>>): boolean;
16
+ draw(e: Frozen<Args<'ui.render'>>): RenderElement;
17
+ closed(id: string): void;
18
+ consent(consent: Consent): Promise<boolean>;
19
+ options(missing: readonly (readonly [string, Option])[], save: (key: string, value: string) => Promise<string | undefined>): Promise<boolean>;
20
+ step<State extends object>(mod: Mod<State>, step: Step<State>, index: number, total: number): Promise<boolean>;
21
+ close(): Promise<void>;
22
+ };
23
+ export declare function createInstaller(name: string, claude: () => Claude): Installer;
24
+ //# sourceMappingURL=installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/runtime/installer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAiB,MAAM,aAAa,CAAA;AAC7E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAI3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AAOD,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAA;IAC5C,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,aAAa,CAAA;IACjD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7I,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,SAAS,CAsI7E"}