windows-development-sandbox 1.1.0 → 1.4.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.
Files changed (187) hide show
  1. package/README.md +3 -2
  2. package/config.example.json +38 -5
  3. package/dist/cli/args.d.ts +3 -0
  4. package/dist/cli/args.js +3 -1
  5. package/dist/cli/commands/accounts.js +2 -2
  6. package/dist/cli/commands/agent-commands.js +1 -1
  7. package/dist/cli/commands/context.js +2 -1
  8. package/dist/cli/commands/data/clean.js +6 -1
  9. package/dist/cli/commands/data/collect.js +3 -3
  10. package/dist/cli/commands/index.d.ts +1 -1
  11. package/dist/cli/commands/index.js +1 -1
  12. package/dist/cli/commands/launch.js +36 -3
  13. package/dist/cli/commands/maintenance.d.ts +2 -0
  14. package/dist/cli/commands/maintenance.js +35 -2
  15. package/dist/cli/commands/sessions.js +3 -3
  16. package/dist/cli/commands/user-config.js +1 -1
  17. package/dist/cli/index.js +6 -4
  18. package/dist/cli/manifest.js +5 -0
  19. package/dist/cli/menu/apps.d.ts +21 -0
  20. package/dist/cli/menu/apps.js +122 -0
  21. package/dist/cli/menu/command-form.d.ts +28 -0
  22. package/dist/cli/menu/command-form.js +219 -0
  23. package/dist/cli/menu/index.d.ts +26 -0
  24. package/dist/cli/menu/index.js +180 -0
  25. package/dist/cli/menu/settings/categories.d.ts +9 -0
  26. package/dist/cli/menu/settings/categories.js +28 -0
  27. package/dist/cli/menu/settings/config-file.d.ts +40 -0
  28. package/dist/cli/menu/settings/config-file.js +71 -0
  29. package/dist/cli/menu/settings/editors.d.ts +30 -0
  30. package/dist/cli/menu/settings/editors.js +305 -0
  31. package/dist/cli/menu/settings/index.d.ts +32 -0
  32. package/dist/cli/menu/settings/index.js +136 -0
  33. package/dist/cli/menu/settings/schema-node.d.ts +62 -0
  34. package/dist/cli/menu/settings/schema-node.js +104 -0
  35. package/dist/cli/{menu-multi.d.ts → menu/several.d.ts} +10 -2
  36. package/dist/cli/menu/several.js +102 -0
  37. package/dist/cli/tui/app.d.ts +39 -0
  38. package/dist/cli/tui/app.js +52 -0
  39. package/dist/cli/tui/dialogs.d.ts +57 -0
  40. package/dist/cli/tui/dialogs.js +162 -0
  41. package/dist/cli/tui/frame.d.ts +18 -0
  42. package/dist/cli/tui/frame.js +27 -0
  43. package/dist/cli/tui/index.d.ts +7 -0
  44. package/dist/cli/tui/index.js +8 -0
  45. package/dist/cli/tui/list-screen.d.ts +101 -0
  46. package/dist/cli/tui/list-screen.js +299 -0
  47. package/dist/cli/tui/terminal.d.ts +37 -0
  48. package/dist/cli/tui/terminal.js +89 -0
  49. package/dist/cli/tui/text.d.ts +20 -0
  50. package/dist/cli/tui/text.js +105 -0
  51. package/dist/cli/tui/theme.d.ts +45 -0
  52. package/dist/cli/tui/theme.js +30 -0
  53. package/dist/core/config.js +9 -6
  54. package/dist/core/local-config.d.ts +15 -0
  55. package/dist/core/local-config.js +63 -4
  56. package/dist/core/schema/apps.d.ts +40 -0
  57. package/dist/core/schema/apps.js +27 -0
  58. package/dist/core/schema/config.d.ts +66 -1
  59. package/dist/core/schema/config.js +18 -6
  60. package/dist/core/schema/profile.d.ts +5 -0
  61. package/dist/core/schema/profile.js +4 -2
  62. package/dist/core/schema/sandbox.d.ts +28 -0
  63. package/dist/core/schema/sandbox.js +28 -0
  64. package/dist/core/schema/toolchains.d.ts +3 -0
  65. package/dist/core/schema/toolchains.js +6 -1
  66. package/dist/core/types.d.ts +6 -0
  67. package/dist/core/util.d.ts +2 -0
  68. package/dist/core/util.js +2 -0
  69. package/dist/core/workspace.js +22 -5
  70. package/dist/guest/apps/index.d.ts +10 -0
  71. package/dist/guest/apps/index.js +206 -0
  72. package/dist/guest/apps/resolvers.d.ts +8 -0
  73. package/dist/guest/apps/resolvers.js +90 -0
  74. package/dist/guest/audit-parse.d.ts +7 -0
  75. package/dist/guest/audit-parse.js +24 -0
  76. package/dist/guest/audit.d.ts +4 -0
  77. package/dist/guest/audit.js +47 -0
  78. package/dist/guest/daemon/relays.d.ts +1 -0
  79. package/dist/guest/daemon/relays.js +18 -0
  80. package/dist/guest/daemon.js +4 -0
  81. package/dist/guest/env.d.ts +1 -0
  82. package/dist/guest/env.js +3 -1
  83. package/dist/guest/helpers.d.ts +1 -1
  84. package/dist/guest/main.js +15 -2
  85. package/dist/guest/netproxy.d.ts +1 -0
  86. package/dist/guest/netproxy.js +100 -0
  87. package/dist/guest/ptyhost.js +14 -4
  88. package/dist/guest/setup/claude.d.ts +0 -4
  89. package/dist/guest/setup/claude.js +48 -2
  90. package/dist/guest/setup/network.d.ts +9 -0
  91. package/dist/guest/setup/network.js +97 -4
  92. package/dist/guest/setup/user.js +4 -0
  93. package/dist/guest/toolchains/native/mesa.js +13 -3
  94. package/dist/guest/toolchains/plan.d.ts +3 -1
  95. package/dist/guest/toolchains/plan.js +53 -9
  96. package/dist/guest/toolchains/python.js +26 -2
  97. package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
  98. package/dist/guest/toolchains/vscode-profile.js +69 -0
  99. package/dist/guest/toolchains/vscode.d.ts +17 -0
  100. package/dist/guest/toolchains/vscode.js +87 -1
  101. package/dist/host/attach.js +2 -1
  102. package/dist/host/audit/collect.d.ts +34 -0
  103. package/dist/host/audit/collect.js +126 -0
  104. package/dist/host/audit/log.d.ts +46 -0
  105. package/dist/host/audit/log.js +143 -0
  106. package/dist/host/generate.d.ts +6 -0
  107. package/dist/host/generate.js +10 -0
  108. package/dist/host/launch/mappings.js +5 -1
  109. package/dist/host/launch/stage.js +1 -1
  110. package/dist/host/motw.d.ts +17 -0
  111. package/dist/host/motw.js +74 -0
  112. package/dist/host/platform.js +3 -0
  113. package/dist/host/port-forward.d.ts +8 -0
  114. package/dist/host/port-forward.js +37 -0
  115. package/dist/host/repo-guard.d.ts +10 -3
  116. package/dist/host/repo-guard.js +142 -14
  117. package/dist/host/stops.d.ts +67 -0
  118. package/dist/host/stops.js +126 -0
  119. package/dist/host/terminal.d.ts +8 -0
  120. package/dist/host/terminal.js +45 -1
  121. package/dist/host/workspace-view/index.d.ts +20 -0
  122. package/dist/host/workspace-view/index.js +53 -0
  123. package/dist/host/workspace-view/rules.d.ts +26 -0
  124. package/dist/host/workspace-view/rules.js +93 -0
  125. package/dist/host/workspace-view/view.d.ts +50 -0
  126. package/dist/host/workspace-view/view.js +281 -0
  127. package/dist/runtime/daemon.js +1319 -552
  128. package/dist/runtime/db.js +168 -13
  129. package/dist/runtime/main.js +2898 -608
  130. package/dist/runtime/netproxy.js +25936 -0
  131. package/dist/runtime/view.js +272 -23
  132. package/dist/selftest/audit.test.d.ts +2 -0
  133. package/dist/selftest/audit.test.js +57 -0
  134. package/dist/selftest/channel.test.js +7 -0
  135. package/dist/selftest/cli.test.js +47 -17
  136. package/dist/selftest/fixtures.d.ts +45 -0
  137. package/dist/selftest/fixtures.js +3 -1
  138. package/dist/selftest/generate.test.js +7 -2
  139. package/dist/selftest/index.js +5 -2
  140. package/dist/selftest/live-network.test.d.ts +2 -0
  141. package/dist/selftest/live-network.test.js +63 -0
  142. package/dist/selftest/local-config.test.js +16 -3
  143. package/dist/selftest/network.test.js +12 -0
  144. package/dist/selftest/projects.test.js +1 -1
  145. package/dist/selftest/repo-guard.test.js +59 -1
  146. package/dist/selftest/sync.test.js +50 -10
  147. package/dist/selftest/toolchains.test.js +30 -0
  148. package/dist/selftest/workspace-view.test.d.ts +2 -0
  149. package/dist/selftest/workspace-view.test.js +89 -0
  150. package/dist/shared/apps.d.ts +49 -0
  151. package/dist/shared/apps.js +114 -0
  152. package/dist/shared/netpolicy.d.ts +27 -0
  153. package/dist/shared/netpolicy.js +51 -0
  154. package/dist/shared/pty-client.d.ts +9 -4
  155. package/dist/shared/pty-client.js +139 -17
  156. package/dist/shared/rundir.d.ts +2 -0
  157. package/dist/shared/rundir.js +2 -0
  158. package/dist/shared/schema.d.ts +10 -0
  159. package/dist/shared/schema.js +2 -0
  160. package/dist/shared/system.d.ts +1 -0
  161. package/dist/shared/system.js +1 -0
  162. package/dist/sync/claude-mem.js +3 -2
  163. package/dist/sync/history.d.ts +6 -0
  164. package/dist/sync/history.js +26 -11
  165. package/dist/sync/watcher.js +78 -6
  166. package/package.json +2 -1
  167. package/sandbox/cargoshim.cs +56 -0
  168. package/sandbox/cpucap.cs +33 -0
  169. package/sandbox/cpucap.ps1 +4 -35
  170. package/sandbox/logon.cmd +6 -0
  171. package/sandbox/memlog.ps1 +34 -0
  172. package/sandbox/pagefile.cs +31 -0
  173. package/sandbox/pagefile.ps1 +21 -0
  174. package/sandbox/ptyhost.cs +105 -7
  175. package/schemas/config.schema.json +303 -2
  176. package/schemas/sandbox-config.schema.json +126 -12
  177. package/dist/cli/menu-multi.js +0 -82
  178. package/dist/cli/menu.d.ts +0 -14
  179. package/dist/cli/menu.js +0 -290
  180. package/dist/cli/settings/edit.d.ts +0 -13
  181. package/dist/cli/settings/edit.js +0 -19
  182. package/dist/cli/settings/index.d.ts +0 -8
  183. package/dist/cli/settings/index.js +0 -131
  184. package/dist/cli/settings/prompts.d.ts +0 -7
  185. package/dist/cli/settings/prompts.js +0 -48
  186. package/dist/cli/settings/schema.d.ts +0 -33
  187. package/dist/cli/settings/schema.js +0 -29
@@ -1,131 +0,0 @@
1
- // The settings screen in the menu: every setting, generated from the zod schemas (so nothing needs to be listed by
2
- // hand), shown with its current value and description, edited with a fitting prompt and saved to a config file. Two
3
- // files can be edited: your own (all projects) and this folder's .sandbox-config.json (only what a project may set).
4
- // Edits keep comments and formatting, and nothing is written unless the whole file is still valid.
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import { select, Separator } from '@inquirer/prompts';
8
- import _ from 'lodash';
9
- import { DEFAULTS, loadConfig, userConfigPath } from '../../core/config.js';
10
- import { LOCAL_CONFIG_NAME, NEVER_LOOSER } from '../../core/local-config.js';
11
- import { Config as ConfigSchema, LocalOverrides } from '../../core/schema.js';
12
- import { c, clearScreen, pageSizeFor, shortenMiddle, termWidth } from '../colors.js';
13
- import { editConfigText, fileValue } from './edit.js';
14
- import { askValue } from './prompts.js';
15
- import { childrenOf, describeSetting, isSection, unwrap } from './schema.js';
16
- export { editConfigText } from './edit.js';
17
- /** Titles of the top-level sections in the menu. A test makes sure every section of the schema has one. */
18
- export const SECTION_TITLES = {
19
- toolchains: 'Languages and tools', databases: 'Databases', sandbox: 'The sandbox machine (memory, window, GPU, network)', attach: 'Attaching (auto attach, auto hide)', agentCommands: 'Agent commands: claude, codex ... start in a sandbox (Beta)',
20
- provisioning: 'Provisioning and git', downloads: 'Downloads (checksums)', secrets: 'Secrets', claudeProfile: 'Your Claude settings and history', storage: 'Storage',
21
- network: 'Network', hooks: 'Hooks', personalization: 'Desktop look', projects: 'Several projects (project roots, adding folders while running)',
22
- };
23
- /** A value in one short line: maps as their keys, long lists shortened. */
24
- const show = (value) => {
25
- if (value === undefined)
26
- return c.dim('(not set)');
27
- if (typeof value === 'string')
28
- return value;
29
- if (value && typeof value === 'object' && !Array.isArray(value)) {
30
- const keys = Object.keys(value);
31
- return keys.length ? '{ ' + _.truncate(keys.join(', '), { length: 60 }) + ' }' : '{}';
32
- }
33
- return _.truncate(JSON.stringify(value), { length: 70 });
34
- };
35
- const readText = (file) => (fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '');
36
- /** The user's settings with the built-in defaults filled in (what a sandbox would use without a project file). */
37
- const userConfig = (file) => loadConfig(fs.existsSync(file) ? file : undefined).config;
38
- /** What is in effect now: for your own file the merged config, for a project file only what that file sets. */
39
- const currentValue = (session, key, text, config) => session.target === 'project' ? fileValue(text, key) : _.get(config, key);
40
- /** Edits one setting: shows it, asks, saves (or resets it to the default / removes it from the project file). */
41
- const editSetting = async (session, schema, key, config) => {
42
- const { file, target } = session;
43
- const text = readText(file);
44
- const inFile = fileValue(text, key);
45
- const project = target === 'project';
46
- console.log('\n' + c.cyan(key.join('.')) + (describeSetting(schema) ? '\n' + c.dim(describeSetting(schema)) : ''));
47
- if (project && NEVER_LOOSER.includes(key.join('.')))
48
- console.log(c.yellow('A project can only make this stricter than your own setting; a looser value is ignored.'));
49
- console.log('Now: ' + (project ? show(inFile) + c.dim(inFile === undefined ? ' (not set in this project; yours: ' + show(_.get(config, key)) + ')' : ' (set in ' + file + ')') : show(_.get(config, key)) + (inFile === undefined ? c.dim(' (built-in default)') : c.dim(' (set in ' + file + ')'))));
50
- const resetLabel = project ? 'Remove from this project file' : 'Reset to the default (' + show(_.get(DEFAULTS, key)) + ')';
51
- const action = inFile === undefined ? 'change' : await select({ message: 'What now?', choices: [{ name: 'Change it', value: 'change' }, { name: resetLabel, value: 'reset' }, { name: c.dim('< back'), value: 'back' }] });
52
- if (action === 'back')
53
- return;
54
- const answer = action === 'reset' ? { value: undefined } : await askValue(schema, key, project ? inFile ?? _.get(config, key) : _.get(config, key), config);
55
- if (!answer)
56
- return;
57
- // In your own file the default needs no line; an unchanged value needs no save.
58
- const value = !project && _.isEqual(answer.value, _.get(DEFAULTS, key)) ? undefined : answer.value;
59
- if (value === inFile && (project || _.isEqual(answer.value, _.get(config, key)))) {
60
- console.log(c.dim('Unchanged.'));
61
- return;
62
- }
63
- const result = editConfigText(text, key, value, target);
64
- if ('problems' in result) {
65
- console.log(c.red('Not saved: ') + result.problems.join('; '));
66
- return;
67
- }
68
- fs.mkdirSync(path.dirname(file), { recursive: true });
69
- fs.writeFileSync(file, result.text, 'utf8');
70
- console.log(c.green('Saved') + c.dim(' to ' + file + (project ? '.' : '. New sandboxes use it.')));
71
- // The approval is tied to the file's exact contents, so any change makes the project file untrusted again.
72
- if (project)
73
- console.log(c.yellow('This project file is not approved any more: the next launch shows what it applies and asks you to approve it.'));
74
- };
75
- /** Browses one level of settings; sections open, leaves are edited. */
76
- const browse = async (session, schema, key) => {
77
- for (;;) {
78
- const config = userConfig(session.target === 'user' ? session.file : userConfigPath());
79
- const entries = childrenOf(schema);
80
- const text = readText(session.file);
81
- clearScreen();
82
- console.log(c.dim('Editing ' + shortenMiddle(session.file, termWidth() - 10)) + '\n' + c.dim('Values in ') + c.yellow('yellow') + c.dim(' (marked *) are set in that file; the rest are defaults.') + '\n');
83
- const pickName = await select({
84
- message: key.length ? key.join('.') : 'Settings',
85
- pageSize: pageSizeFor(18),
86
- choices: [
87
- ...entries.map(([name, s]) => {
88
- const full = [...key, name];
89
- const changed = fileValue(text, full) !== undefined;
90
- const shown = show(currentValue(session, full, text, config));
91
- const label = isSection(s) ? (key.length ? name : SECTION_TITLES[name] ?? name) + c.dim(' ...') : name.padEnd(18) + ' ' + (changed ? c.yellow(shown) + ' *' : shown);
92
- return { name: label, value: name, description: describeSetting(s) };
93
- }),
94
- new Separator(),
95
- { name: c.dim('< back'), value: '@back' },
96
- ],
97
- });
98
- if (pickName === '@back')
99
- return;
100
- const child = unwrap(schema).shape[pickName];
101
- if (isSection(child))
102
- await browse(session, child, [...key, pickName]);
103
- else
104
- await editSetting(session, child, [...key, pickName], config);
105
- }
106
- };
107
- /**
108
- * The settings screen: first which file to edit (your own, or this folder's project file), then its settings. Your file
109
- * is created when missing; so is the project file once a setting is saved.
110
- */
111
- export const settingsMenu = async (configPath, projectDir = process.cwd()) => {
112
- const sessions = {
113
- user: { target: 'user', file: configPath ?? userConfigPath(), schema: ConfigSchema },
114
- project: { target: 'project', file: path.join(projectDir, LOCAL_CONFIG_NAME), schema: LocalOverrides },
115
- };
116
- for (;;) {
117
- clearScreen();
118
- const which = await select({
119
- message: 'Which settings?',
120
- choices: [
121
- { name: 'Your settings (all projects)', description: sessions.user.file, value: 'user' },
122
- { name: 'This project', description: sessions.project.file + ' (only what a project may set; needs your approval at launch)', value: 'project' },
123
- new Separator(),
124
- { name: c.dim('< back'), value: '@back' },
125
- ],
126
- });
127
- if (which === '@back')
128
- return;
129
- await browse(sessions[which], sessions[which].schema, []);
130
- }
131
- };
@@ -1,7 +0,0 @@
1
- import type { Config } from '../../core/types.js';
2
- import { type Schema } from './schema.js';
3
- export type Answer = {
4
- value: unknown;
5
- } | undefined;
6
- /** `key` is the setting's path; `config` (the user's settings) lets the default agent be picked from the configured ones. */
7
- export declare const askValue: (schema: Schema, key: string[], current: unknown, config?: Config) => Promise<Answer>;
@@ -1,48 +0,0 @@
1
- // The prompt that fits a setting's type. Returns the new value, or undefined to change nothing.
2
- import { input, select } from '@inquirer/prompts';
3
- import { c } from '../colors.js';
4
- import { jsonExample, kind, promptKind, unwrap } from './schema.js';
5
- const CANCEL = '@cancel';
6
- const cancel = { name: c.dim('< cancel'), value: CANCEL };
7
- const pick = async (label, choices, current) => {
8
- const v = await select({ message: label, choices: [...choices, cancel], default: current });
9
- return v === CANCEL ? undefined : { value: v };
10
- };
11
- const typed = async (label, current, validate, convert) => {
12
- const shown = current === undefined ? '' : typeof current === 'string' ? current : JSON.stringify(current);
13
- const answer = await input({ message: label + c.dim(' (empty = cancel)'), default: shown, validate: (v) => (v === '' ? true : validate(v)) });
14
- return answer === '' ? undefined : { value: convert(answer) };
15
- };
16
- const BOOLEANS = [{ name: 'true', value: true }, { name: 'false', value: false }];
17
- /** `key` is the setting's path; `config` (the user's settings) lets the default agent be picked from the configured ones. */
18
- export const askValue = async (schema, key, current, config) => {
19
- const label = key.join('.');
20
- const s = unwrap(schema);
21
- // The default agent is one of the configured ones: a list, not free text.
22
- if (label === 'defaultHarness' && config)
23
- return pick(label, Object.keys(config.harnesses).map((h) => ({ name: h, value: h })), current);
24
- switch (promptKind(s)) {
25
- case 'boolean': return pick(label, BOOLEANS, current);
26
- case 'enum': return pick(label, Object.values(s._zod.def.entries ?? {}).map((v) => ({ name: String(v), value: v })), current);
27
- case 'union': {
28
- const options = s._zod.def.options ?? [];
29
- const literals = options.filter((o) => kind(o) === 'literal').flatMap((o) => unwrap(o)._zod.def.values ?? []);
30
- const other = options.find((o) => !['literal', 'boolean'].includes(kind(o)));
31
- const choices = [...(options.some((o) => kind(o) === 'boolean') ? BOOLEANS : []), ...literals.map((v) => ({ name: String(v), value: v }))];
32
- if (!other)
33
- return pick(label, choices, current);
34
- const chosen = await pick(label, [...choices, { name: 'a ' + kind(other) + ' ...', value: '@other' }], current);
35
- return chosen?.value === '@other' ? askValue(other, key, current) : chosen;
36
- }
37
- case 'number': return typed(label, current, (v) => (Number.isFinite(Number(v)) ? true : 'Enter a number, e.g. 8192.'), Number);
38
- case 'string': return typed(label, current, () => true, (v) => v);
39
- case 'string-list': return typed(label + c.dim(' (comma separated, [] = none)'), current, () => true, (v) => (v.trim() === '[]' ? [] : v.split(',').map((x) => x.trim()).filter(Boolean)));
40
- default: return typed(label + c.dim(' (JSON, e.g. ' + jsonExample(key, s) + ')'), current, (v) => { try {
41
- JSON.parse(v);
42
- return true;
43
- }
44
- catch {
45
- return 'valid JSON';
46
- } }, (v) => JSON.parse(v));
47
- }
48
- };
@@ -1,33 +0,0 @@
1
- import type { z } from 'zod';
2
- export type Schema = z.ZodType & {
3
- _zod: {
4
- def: {
5
- type: string;
6
- options?: Schema[];
7
- values?: unknown[];
8
- entries?: Record<string, string>;
9
- element?: Schema;
10
- innerType?: Schema;
11
- };
12
- };
13
- shape?: Record<string, Schema>;
14
- description?: string;
15
- };
16
- /** optional()/default() wrappers hide the real type. */
17
- export declare const unwrap: (s: Schema) => Schema;
18
- export declare const kind: (s: Schema) => string;
19
- export declare const isSection: (s: Schema) => boolean;
20
- /** The settings one level below a section (or the top level), in schema order. */
21
- export declare const childrenOf: (schema: Schema) => [string, Schema][];
22
- /** The description, which `.partial()` and `.optional()` wrappers may carry on the inner schema instead. */
23
- export declare const describeSetting: (s: Schema) => string | undefined;
24
- /** Every setting that is edited with a prompt of its own (everything that is not a section), with its path. */
25
- export declare const leavesOf: (schema: Schema, key?: string[]) => {
26
- key: string[];
27
- schema: Schema;
28
- }[];
29
- export type PromptKind = 'boolean' | 'enum' | 'union' | 'number' | 'string' | 'string-list' | 'json';
30
- /** Which prompt edits this setting. Lists of objects and maps (mappings, harnesses, environment) fall back to JSON. */
31
- export declare const promptKind: (schema: Schema) => PromptKind;
32
- /** A one-line example of the JSON a setting expects, shown in its prompt. */
33
- export declare const jsonExample: (key: string[], schema: Schema) => string;
@@ -1,29 +0,0 @@
1
- /** optional()/default() wrappers hide the real type. */
2
- export const unwrap = (s) => (['optional', 'default', 'nullable'].includes(s._zod.def.type) && s._zod.def.innerType ? unwrap(s._zod.def.innerType) : s);
3
- export const kind = (s) => unwrap(s)._zod.def.type;
4
- export const isSection = (s) => kind(s) === 'object' && Boolean(unwrap(s).shape);
5
- /** Settings not worth editing here (the schema link). */
6
- const HIDDEN = new Set(['$schema']);
7
- /** The settings one level below a section (or the top level), in schema order. */
8
- export const childrenOf = (schema) => Object.entries(unwrap(schema).shape ?? {}).filter(([name]) => !HIDDEN.has(name));
9
- /** The description, which `.partial()` and `.optional()` wrappers may carry on the inner schema instead. */
10
- export const describeSetting = (s) => s.description ?? unwrap(s).description;
11
- /** Every setting that is edited with a prompt of its own (everything that is not a section), with its path. */
12
- export const leavesOf = (schema, key = []) => childrenOf(schema).flatMap(([name, child]) => (isSection(child) ? leavesOf(child, [...key, name]) : [{ key: [...key, name], schema: child }]));
13
- /** Which prompt edits this setting. Lists of objects and maps (mappings, harnesses, environment) fall back to JSON. */
14
- export const promptKind = (schema) => {
15
- const s = unwrap(schema);
16
- const type = s._zod.def.type;
17
- if (type === 'boolean' || type === 'enum' || type === 'union' || type === 'number' || type === 'string')
18
- return type;
19
- if (type === 'array' && s._zod.def.element && kind(s._zod.def.element) === 'string')
20
- return 'string-list';
21
- return 'json';
22
- };
23
- const JSON_EXAMPLES = {
24
- harnesses: '{"mytool": {"npmPackage": "my-cli", "npmBin": "my-cli", "arguments": [], "environment": {}}}',
25
- mappings: '[{"host": "C:\\\\data", "guest": "C:\\\\Data", "readOnly": true}]',
26
- environment: '{"NPM_CONFIG_REGISTRY": "https://registry.example.com"}',
27
- };
28
- /** A one-line example of the JSON a setting expects, shown in its prompt. */
29
- export const jsonExample = (key, schema) => JSON_EXAMPLES[key[key.length - 1]] ?? (unwrap(schema)._zod.def.type === 'array' ? '["value", ...]' : '{"name": "value"}');