@zodal/dials-codegen 0.1.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/README.md +30 -0
- package/dist/index.cjs +249 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +216 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @zodal/dials-codegen
|
|
2
|
+
|
|
3
|
+
Machine-interface emit for [zodal-dials](https://github.com/i2mint/zodal-dials): a **JSON Schema** (editor autocomplete/validation for the settings file), an **AI prompt**, and **CLI helpers**.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i @zodal/dials-codegen @zodal/dials-core
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { toJsonSchema, toPrompt, runCli } from '@zodal/dials-codegen';
|
|
11
|
+
|
|
12
|
+
// 1. JSON Schema — point a settings file's "$schema" at it for autocomplete (flat dotted keyspace,
|
|
13
|
+
// secret values redacted, $schema pointer allowed):
|
|
14
|
+
writeFile('dials.schema.json', JSON.stringify(toJsonSchema(dials, { $id: 'https://x/dials.schema.json' })));
|
|
15
|
+
|
|
16
|
+
// 2. AI prompt — let an assistant help the user configure:
|
|
17
|
+
toPrompt(dials); // "# Settings\n- `editor.theme` (enum: light | dark | system, default "system") — …"
|
|
18
|
+
|
|
19
|
+
// 3. CLI — get/set/list with provenance, secrets masked:
|
|
20
|
+
runCli(['list', '--show-origin'], { dials, stack, layer });
|
|
21
|
+
// editor.fontSize = 16 (user)
|
|
22
|
+
// editor.theme = "dark" (workspace)
|
|
23
|
+
runCli(['set', 'editor.fontSize', '18'], ctx); // validates against the field; returns a new layer
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **`toJsonSchema`** — secret defaults/enums/consts redacted; `required` dropped (overrides are optional); a `$schema` property added so the file's own pointer validates.
|
|
27
|
+
- **`toPrompt`** — secrets marked `[secret]`, never carrying their value.
|
|
28
|
+
- **CLI** (`runCli`/`formatList`/`formatGet`/`coerceByType`) — pure & IO-free; `list --show-origin` surfaces cascade provenance (à la `git config --show-origin`); `set` validates and never echoes a secret; secrets always masked.
|
|
29
|
+
|
|
30
|
+
Part of the [zodal-dials](https://github.com/i2mint/zodal-dials) ecosystem.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
coerceByType: () => coerceByType,
|
|
24
|
+
describeForCodegen: () => describeForCodegen,
|
|
25
|
+
formatGet: () => formatGet,
|
|
26
|
+
formatList: () => formatList,
|
|
27
|
+
runCli: () => runCli,
|
|
28
|
+
toJsonSchema: () => toJsonSchema,
|
|
29
|
+
toPrompt: () => toPrompt
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/json-schema.ts
|
|
34
|
+
var import_zod = require("zod");
|
|
35
|
+
|
|
36
|
+
// src/introspect.ts
|
|
37
|
+
var import_dials_core = require("@zodal/dials-core");
|
|
38
|
+
var import_core = require("@zodal/core");
|
|
39
|
+
function safe(fn) {
|
|
40
|
+
try {
|
|
41
|
+
return fn();
|
|
42
|
+
} catch {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function describeForCodegen(dials) {
|
|
47
|
+
const shape = (0, import_dials_core.getObjectShape)(dials.schema);
|
|
48
|
+
return dials.keys.map((key) => {
|
|
49
|
+
const field = shape[key];
|
|
50
|
+
const meta = field ? (0, import_dials_core.readMeta)(field) : {};
|
|
51
|
+
const enumValues = field ? safe(() => (0, import_core.getEnumValues)(field)) : void 0;
|
|
52
|
+
return {
|
|
53
|
+
key,
|
|
54
|
+
type: field ? (0, import_dials_core.baseType)(field) : "unknown",
|
|
55
|
+
default: dials.defaults[key],
|
|
56
|
+
enumValues: enumValues && enumValues.length > 0 ? enumValues : void 0,
|
|
57
|
+
description: typeof meta.description === "string" ? meta.description : void 0,
|
|
58
|
+
sensitivity: dials.sensitivityFor(key)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/json-schema.ts
|
|
64
|
+
function fallbackSchema(dials) {
|
|
65
|
+
const properties = {};
|
|
66
|
+
for (const field of describeForCodegen(dials)) {
|
|
67
|
+
const prop = {};
|
|
68
|
+
if (field.type === "number" || field.type === "boolean") prop.type = field.type;
|
|
69
|
+
else if (field.type === "enum") {
|
|
70
|
+
prop.type = "string";
|
|
71
|
+
if (field.enumValues) prop.enum = field.enumValues;
|
|
72
|
+
} else if (field.type === "array") prop.type = "array";
|
|
73
|
+
else if (field.type === "object") prop.type = "object";
|
|
74
|
+
else prop.type = "string";
|
|
75
|
+
if (field.default !== void 0) prop.default = field.default;
|
|
76
|
+
if (field.description) prop.description = field.description;
|
|
77
|
+
properties[field.key] = prop;
|
|
78
|
+
}
|
|
79
|
+
return { $schema: "https://json-schema.org/draft/2020-12/schema", type: "object", properties };
|
|
80
|
+
}
|
|
81
|
+
function toJsonSchema(dials, options = {}) {
|
|
82
|
+
let schema;
|
|
83
|
+
try {
|
|
84
|
+
schema = import_zod.z.toJSONSchema(dials.schema);
|
|
85
|
+
} catch {
|
|
86
|
+
schema = fallbackSchema(dials);
|
|
87
|
+
}
|
|
88
|
+
if (!options.keepRequired) delete schema.required;
|
|
89
|
+
schema.additionalProperties = options.additionalProperties ?? false;
|
|
90
|
+
if (options.$id) schema.$id = options.$id;
|
|
91
|
+
if (options.title) schema.title = options.title;
|
|
92
|
+
const properties = schema.properties;
|
|
93
|
+
if (properties) {
|
|
94
|
+
for (const key of Object.keys(properties)) {
|
|
95
|
+
if (dials.sensitivityFor(key) === "secret") {
|
|
96
|
+
const prop = properties[key];
|
|
97
|
+
delete prop.default;
|
|
98
|
+
delete prop.const;
|
|
99
|
+
delete prop.enum;
|
|
100
|
+
delete prop.examples;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (schema.additionalProperties === false && !properties["$schema"]) {
|
|
104
|
+
properties["$schema"] = { type: "string" };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return schema;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/prompt.ts
|
|
111
|
+
function toPrompt(dials, options = {}) {
|
|
112
|
+
const fields = describeForCodegen(dials);
|
|
113
|
+
const lines = [];
|
|
114
|
+
lines.push(`# ${options.title ?? "Settings"}`);
|
|
115
|
+
lines.push("");
|
|
116
|
+
lines.push(
|
|
117
|
+
`This system exposes ${fields.length} settings. Each is a typed, named parameter identified by a dotted key. To change one, supply its key and a value of the stated type (for enums, one of the listed values).`
|
|
118
|
+
);
|
|
119
|
+
lines.push("");
|
|
120
|
+
for (const field of fields) {
|
|
121
|
+
let line = `- \`${field.key}\` (${field.type}`;
|
|
122
|
+
if (field.enumValues) line += `: ${field.enumValues.join(" | ")}`;
|
|
123
|
+
if (field.sensitivity !== "secret" && field.default !== void 0) line += `, default ${JSON.stringify(field.default)}`;
|
|
124
|
+
line += ")";
|
|
125
|
+
if (field.sensitivity === "secret") line += " [secret]";
|
|
126
|
+
if (field.description) line += ` \u2014 ${field.description}`;
|
|
127
|
+
lines.push(line);
|
|
128
|
+
}
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/cli.ts
|
|
133
|
+
var import_dials_core2 = require("@zodal/dials-core");
|
|
134
|
+
function coerceByType(type, raw) {
|
|
135
|
+
switch (type) {
|
|
136
|
+
case "number": {
|
|
137
|
+
const n = Number(raw);
|
|
138
|
+
if (Number.isNaN(n)) throw new Error(`"${raw}" is not a number`);
|
|
139
|
+
return n;
|
|
140
|
+
}
|
|
141
|
+
case "boolean": {
|
|
142
|
+
const t = raw.toLowerCase();
|
|
143
|
+
if (t === "true" || t === "1" || t === "yes") return true;
|
|
144
|
+
if (t === "false" || t === "0" || t === "no") return false;
|
|
145
|
+
throw new Error(`"${raw}" is not a boolean (use true/false)`);
|
|
146
|
+
}
|
|
147
|
+
case "object":
|
|
148
|
+
case "array":
|
|
149
|
+
return JSON.parse(raw);
|
|
150
|
+
case "string":
|
|
151
|
+
case "enum":
|
|
152
|
+
return raw;
|
|
153
|
+
default:
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(raw);
|
|
156
|
+
} catch {
|
|
157
|
+
return raw;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function typeOf(dials, key) {
|
|
162
|
+
const field = (0, import_dials_core2.getObjectShape)(dials.schema)[key];
|
|
163
|
+
return field ? (0, import_dials_core2.baseType)(field) : "unknown";
|
|
164
|
+
}
|
|
165
|
+
function formatValue(value) {
|
|
166
|
+
if ((0, import_dials_core2.isSecretRef)(value)) return value.masked;
|
|
167
|
+
return JSON.stringify(value);
|
|
168
|
+
}
|
|
169
|
+
function resolveContext(ctx) {
|
|
170
|
+
const stack = [...ctx.stack ?? [], { scope: ctx.scope ?? "user", layer: ctx.layer }];
|
|
171
|
+
return ctx.dials.resolve(stack, { maskSecrets: true });
|
|
172
|
+
}
|
|
173
|
+
function formatList(ctx, options = {}) {
|
|
174
|
+
const result = resolveContext(ctx);
|
|
175
|
+
const lines = [];
|
|
176
|
+
const extras = Object.keys(result.provenance).filter((k) => !ctx.dials.keys.includes(k));
|
|
177
|
+
for (const key of [...ctx.dials.keys, ...extras]) {
|
|
178
|
+
const prov = result.provenance[key];
|
|
179
|
+
if (!prov) continue;
|
|
180
|
+
if (options.modifiedOnly && prov.winningScope === "default") continue;
|
|
181
|
+
let line = `${key} = ${formatValue(result.effective[key])}`;
|
|
182
|
+
if (options.showOrigin) line += ` (${prov.winningScope}${prov.managed ? ", policy" : ""})`;
|
|
183
|
+
lines.push(line);
|
|
184
|
+
}
|
|
185
|
+
return lines.join("\n");
|
|
186
|
+
}
|
|
187
|
+
function formatGet(ctx, key) {
|
|
188
|
+
if (!ctx.dials.keys.includes(key)) return `unknown setting: ${key}`;
|
|
189
|
+
const result = resolveContext(ctx);
|
|
190
|
+
const prov = result.provenance[key];
|
|
191
|
+
if (!prov) return `${key} is unset`;
|
|
192
|
+
const shadow = prov.shadowed.length > 0 ? ` [shadows ${prov.shadowed.map((s) => s.scope).join(", ")}]` : "";
|
|
193
|
+
return `${key} = ${formatValue(result.effective[key])} (${prov.winningScope}${prov.managed ? ", policy" : ""})${shadow}`;
|
|
194
|
+
}
|
|
195
|
+
function runCli(argv, ctx) {
|
|
196
|
+
const [command, ...rest] = argv;
|
|
197
|
+
const flags = new Set(rest.filter((a) => a.startsWith("--")));
|
|
198
|
+
const positional = rest.filter((a) => !a.startsWith("--"));
|
|
199
|
+
const layer = ctx.layer;
|
|
200
|
+
switch (command) {
|
|
201
|
+
case "list":
|
|
202
|
+
return { output: formatList(ctx, { showOrigin: flags.has("--show-origin"), modifiedOnly: flags.has("--modified") }), layer };
|
|
203
|
+
case "get": {
|
|
204
|
+
const key = positional[0];
|
|
205
|
+
if (!key) return { output: "usage: get <key>", layer };
|
|
206
|
+
return { output: formatGet(ctx, key), layer };
|
|
207
|
+
}
|
|
208
|
+
case "set": {
|
|
209
|
+
const [key, raw] = positional;
|
|
210
|
+
if (key === void 0 || raw === void 0) return { output: "usage: set <key> <value>", layer };
|
|
211
|
+
if (!ctx.dials.keys.includes(key)) return { output: `unknown setting: ${key}`, layer };
|
|
212
|
+
let value;
|
|
213
|
+
try {
|
|
214
|
+
value = coerceByType(typeOf(ctx.dials, key), raw);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return { output: `invalid value for ${key}: ${error.message}`, layer };
|
|
217
|
+
}
|
|
218
|
+
const field = (0, import_dials_core2.getObjectShape)(ctx.dials.schema)[key];
|
|
219
|
+
if (field) {
|
|
220
|
+
const parsed = field.safeParse(value);
|
|
221
|
+
if (!parsed.success) {
|
|
222
|
+
return { output: `invalid value for ${key}: ${parsed.error.issues[0]?.message ?? "validation failed"}`, layer };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const shown = ctx.dials.sensitivityFor(key) === "secret" ? "\u2022\u2022\u2022\u2022 (set)" : JSON.stringify(value);
|
|
226
|
+
return { output: `set ${key} = ${shown}`, layer: { ...layer, [key]: value } };
|
|
227
|
+
}
|
|
228
|
+
case "unset": {
|
|
229
|
+
const key = positional[0];
|
|
230
|
+
if (!key) return { output: "usage: unset <key>", layer };
|
|
231
|
+
const next = { ...layer };
|
|
232
|
+
delete next[key];
|
|
233
|
+
return { output: `unset ${key}`, layer: next };
|
|
234
|
+
}
|
|
235
|
+
default:
|
|
236
|
+
return { output: `usage: dials <list|get|set|unset> [...] (got "${command ?? ""}")`, layer };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
240
|
+
0 && (module.exports = {
|
|
241
|
+
coerceByType,
|
|
242
|
+
describeForCodegen,
|
|
243
|
+
formatGet,
|
|
244
|
+
formatList,
|
|
245
|
+
runCli,
|
|
246
|
+
toJsonSchema,
|
|
247
|
+
toPrompt
|
|
248
|
+
});
|
|
249
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/json-schema.ts","../src/introspect.ts","../src/prompt.ts","../src/cli.ts"],"sourcesContent":["/**\n * @zodal/dials-codegen — machine-interface emit for zodal-dials.\n *\n * - `toJsonSchema(dials, options?)` — a JSON Schema for editor autocomplete/validation of a settings\n * file (`$schema` target; flat dotted keyspace; secret defaults redacted).\n * - `toPrompt(dials, options?)` — an AI/LLM-consumable description of the settings.\n * - CLI helpers — `runCli`/`formatList`/`formatGet`/`coerceByType`: the logic behind a\n * `dials get|set|list --show-origin|unset` command (provenance-aware, secrets masked, IO-free).\n */\n\nexport { toJsonSchema } from './json-schema.js';\nexport type { ToJsonSchemaOptions } from './json-schema.js';\n\nexport { toPrompt } from './prompt.js';\nexport type { ToPromptOptions } from './prompt.js';\n\nexport { runCli, formatList, formatGet, coerceByType } from './cli.js';\nexport type { CliContext, RunCliResult, ListOptions } from './cli.js';\n\nexport { describeForCodegen } from './introspect.js';\nexport type { CodegenField } from './introspect.js';\n","/**\n * Emit a JSON Schema for a settings surface — point a settings file's `$schema` at it and any editor\n * gives autocomplete + validation (the VS Code `settings.json` experience). The schema describes the\n * FLAT dotted keyspace; `required` is dropped by default (a settings file is a sparse override, every\n * key optional) and secret defaults are redacted (the schema carries structure, not secret values).\n */\n\nimport { z } from 'zod';\nimport type { DialsDefinition } from '@zodal/dials-core';\nimport { describeForCodegen } from './introspect.js';\n\nexport interface ToJsonSchemaOptions {\n $id?: string;\n title?: string;\n /** Allow keys not in the schema. Default: false (flags typos in a settings file). */\n additionalProperties?: boolean;\n /** Keep `required` (default: false — settings overrides are all optional). */\n keepRequired?: boolean;\n}\n\nfunction fallbackSchema<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): Record<string, unknown> {\n const properties: Record<string, Record<string, unknown>> = {};\n for (const field of describeForCodegen(dials)) {\n const prop: Record<string, unknown> = {};\n if (field.type === 'number' || field.type === 'boolean') prop.type = field.type;\n else if (field.type === 'enum') {\n prop.type = 'string';\n if (field.enumValues) prop.enum = field.enumValues;\n } else if (field.type === 'array') prop.type = 'array';\n else if (field.type === 'object') prop.type = 'object';\n else prop.type = 'string';\n if (field.default !== undefined) prop.default = field.default;\n if (field.description) prop.description = field.description;\n properties[field.key] = prop;\n }\n return { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties };\n}\n\n/** Build a JSON Schema describing the settings (for editor autocomplete/validation). */\nexport function toJsonSchema<T extends z.ZodObject<z.ZodRawShape>>(\n dials: DialsDefinition<T>,\n options: ToJsonSchemaOptions = {},\n): Record<string, unknown> {\n let schema: Record<string, unknown>;\n try {\n // z.toJSONSchema throws on unrepresentable types — fall back to a hand-built schema.\n schema = z.toJSONSchema(dials.schema) as Record<string, unknown>;\n } catch {\n schema = fallbackSchema(dials);\n }\n\n if (!options.keepRequired) delete schema.required;\n schema.additionalProperties = options.additionalProperties ?? false;\n if (options.$id) schema.$id = options.$id;\n if (options.title) schema.title = options.title;\n\n const properties = schema.properties as Record<string, Record<string, unknown>> | undefined;\n if (properties) {\n // Redact ALL value-bearing keywords for secret keys — not just `default`, but `const`/`enum`/\n // `examples` too — so a secret's value (or value set) never appears in the emitted schema.\n for (const key of Object.keys(properties)) {\n if (dials.sensitivityFor(key) === 'secret') {\n const prop = properties[key];\n delete prop.default;\n delete prop.const;\n delete prop.enum;\n delete prop.examples;\n }\n }\n // Allow the conventional `\"$schema\": \"…\"` pointer the settings file carries, so the schema does\n // not reject the very file it is meant to validate under `additionalProperties: false`.\n if (schema.additionalProperties === false && !properties['$schema']) {\n properties['$schema'] = { type: 'string' };\n }\n }\n return schema;\n}\n","/**\n * Internal per-key descriptor for codegen (type, default, enum values, description, sensitivity),\n * derived from a `DialsDefinition` via dials-core introspection + `@zodal/core` enum extraction.\n */\n\nimport type { z } from 'zod';\nimport { baseType, getObjectShape, readMeta } from '@zodal/dials-core';\nimport type { DialsDefinition, Sensitivity } from '@zodal/dials-core';\nimport { getEnumValues } from '@zodal/core';\n\nexport interface CodegenField {\n key: string;\n type: string;\n default: unknown;\n enumValues?: string[];\n description?: string;\n sensitivity: Sensitivity;\n}\n\nfunction safe<R>(fn: () => R): R | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/** Describe every setting for codegen consumers (schema/prompt/CLI). */\nexport function describeForCodegen<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): CodegenField[] {\n const shape = getObjectShape(dials.schema);\n return dials.keys.map((key) => {\n const field = shape[key];\n const meta = field ? readMeta(field) : {};\n const enumValues = field ? safe(() => getEnumValues(field) as unknown as string[]) : undefined;\n return {\n key,\n type: field ? baseType(field) : 'unknown',\n default: dials.defaults[key],\n enumValues: enumValues && enumValues.length > 0 ? enumValues : undefined,\n description: typeof meta.description === 'string' ? meta.description : undefined,\n sensitivity: dials.sensitivityFor(key),\n };\n });\n}\n","/**\n * Emit an AI/LLM-consumable description of a settings surface — so an assistant can help a user\n * configure the system (\"set the editor theme to dark\", \"what controls connection pooling?\"). Lists\n * each setting with its type, allowed values, default, and description. Secrets are marked `[secret]`\n * and never carry their default value.\n */\n\nimport type { z } from 'zod';\nimport type { DialsDefinition } from '@zodal/dials-core';\nimport { describeForCodegen } from './introspect.js';\n\nexport interface ToPromptOptions {\n /** A heading for the settings block. Default: 'Settings'. */\n title?: string;\n}\n\n/** Build an LLM-consumable description of the settings. */\nexport function toPrompt<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options: ToPromptOptions = {}): string {\n const fields = describeForCodegen(dials);\n const lines: string[] = [];\n lines.push(`# ${options.title ?? 'Settings'}`);\n lines.push('');\n lines.push(\n `This system exposes ${fields.length} settings. Each is a typed, named parameter identified by a dotted key. ` +\n 'To change one, supply its key and a value of the stated type (for enums, one of the listed values).',\n );\n lines.push('');\n for (const field of fields) {\n let line = `- \\`${field.key}\\` (${field.type}`;\n if (field.enumValues) line += `: ${field.enumValues.join(' | ')}`;\n if (field.sensitivity !== 'secret' && field.default !== undefined) line += `, default ${JSON.stringify(field.default)}`;\n line += ')';\n if (field.sensitivity === 'secret') line += ' [secret]';\n if (field.description) line += ` — ${field.description}`;\n lines.push(line);\n }\n return lines.join('\\n');\n}\n","/**\n * Headless CLI helpers for zodal-dials — the logic behind a `dials get|set|list|unset` command,\n * pure and IO-free (the consumer wires stores + argv + stdout). `list --show-origin` surfaces the\n * cascade's PROVENANCE per key (the differentiator, à la `git config --show-origin`). Secrets are\n * always shown masked. `set`/`unset` return a NEW editable layer; they never mutate the input.\n */\n\nimport type { z } from 'zod';\nimport { baseType, getObjectShape, isSecretRef } from '@zodal/dials-core';\nimport type { DialsDefinition, Layer, ScopedLayer, SettingKey } from '@zodal/dials-core';\n\nexport interface CliContext<T extends z.ZodObject<z.ZodRawShape>> {\n dials: DialsDefinition<T>;\n /** The ordered scope stack BELOW the editable layer (defaults are prepended by resolve). */\n stack?: ScopedLayer[];\n /** The editable (user) layer being get/set. */\n layer: Layer;\n /** The scope id of the editable layer. Default: 'user'. */\n scope?: string;\n}\n\nexport interface RunCliResult {\n output: string;\n /** The (possibly updated) editable layer — changed by `set`/`unset`, unchanged otherwise. */\n layer: Layer;\n}\n\n/** Coerce a raw CLI string to a value of the given base type. Throws on a malformed number/JSON. */\nexport function coerceByType(type: string, raw: string): unknown {\n switch (type) {\n case 'number': {\n const n = Number(raw);\n if (Number.isNaN(n)) throw new Error(`\"${raw}\" is not a number`);\n return n;\n }\n case 'boolean': {\n const t = raw.toLowerCase();\n if (t === 'true' || t === '1' || t === 'yes') return true;\n if (t === 'false' || t === '0' || t === 'no') return false;\n throw new Error(`\"${raw}\" is not a boolean (use true/false)`);\n }\n case 'object':\n case 'array':\n return JSON.parse(raw);\n case 'string':\n case 'enum':\n return raw;\n default:\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n}\n\nfunction typeOf<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, key: SettingKey): string {\n const field = getObjectShape(dials.schema)[key];\n return field ? baseType(field) : 'unknown';\n}\n\nfunction formatValue(value: unknown): string {\n if (isSecretRef(value)) return value.masked;\n return JSON.stringify(value);\n}\n\nfunction resolveContext<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>): ReturnType<DialsDefinition<T>['resolve']> {\n const stack: ScopedLayer[] = [...(ctx.stack ?? []), { scope: ctx.scope ?? 'user', layer: ctx.layer }];\n return ctx.dials.resolve(stack, { maskSecrets: true });\n}\n\nexport interface ListOptions {\n showOrigin?: boolean;\n modifiedOnly?: boolean;\n}\n\n/** Format the effective settings as lines (`key = value`, optionally `(scope)`). Secrets masked. */\nexport function formatList<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, options: ListOptions = {}): string {\n const result = resolveContext(ctx);\n const lines: string[] = [];\n // schema keys first, then any ad-hoc (out-of-schema) keys that resolved — so nothing is hidden.\n const extras = Object.keys(result.provenance).filter((k) => !ctx.dials.keys.includes(k));\n for (const key of [...ctx.dials.keys, ...extras]) {\n const prov = result.provenance[key];\n if (!prov) continue; // unset\n if (options.modifiedOnly && prov.winningScope === 'default') continue;\n let line = `${key} = ${formatValue(result.effective[key])}`;\n if (options.showOrigin) line += `\\t(${prov.winningScope}${prov.managed ? ', policy' : ''})`;\n lines.push(line);\n }\n return lines.join('\\n');\n}\n\n/** Format a single setting's effective value + origin. */\nexport function formatGet<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, key: SettingKey): string {\n if (!ctx.dials.keys.includes(key)) return `unknown setting: ${key}`;\n const result = resolveContext(ctx);\n const prov = result.provenance[key];\n if (!prov) return `${key} is unset`;\n const shadow = prov.shadowed.length > 0 ? ` [shadows ${prov.shadowed.map((s) => s.scope).join(', ')}]` : '';\n return `${key} = ${formatValue(result.effective[key])}\\t(${prov.winningScope}${prov.managed ? ', policy' : ''})${shadow}`;\n}\n\n/**\n * Dispatch a CLI command against the context. `argv` is the command + args (e.g. `['set',\n * 'editor.fontSize', '16']`). Returns the output text and the (possibly new) editable layer. Pure.\n */\nexport function runCli<T extends z.ZodObject<z.ZodRawShape>>(argv: string[], ctx: CliContext<T>): RunCliResult {\n const [command, ...rest] = argv;\n const flags = new Set(rest.filter((a) => a.startsWith('--')));\n const positional = rest.filter((a) => !a.startsWith('--'));\n const layer = ctx.layer;\n\n switch (command) {\n case 'list':\n return { output: formatList(ctx, { showOrigin: flags.has('--show-origin'), modifiedOnly: flags.has('--modified') }), layer };\n case 'get': {\n const key = positional[0];\n if (!key) return { output: 'usage: get <key>', layer };\n return { output: formatGet(ctx, key), layer };\n }\n case 'set': {\n const [key, raw] = positional;\n if (key === undefined || raw === undefined) return { output: 'usage: set <key> <value>', layer };\n if (!ctx.dials.keys.includes(key)) return { output: `unknown setting: ${key}`, layer };\n let value: unknown;\n try {\n value = coerceByType(typeOf(ctx.dials, key), raw);\n } catch (error) {\n return { output: `invalid value for ${key}: ${(error as Error).message}`, layer };\n }\n // Validate the coerced value against the field schema (rejects out-of-enum / out-of-range).\n const field = getObjectShape(ctx.dials.schema)[key];\n if (field) {\n const parsed = field.safeParse(value);\n if (!parsed.success) {\n return { output: `invalid value for ${key}: ${parsed.error.issues[0]?.message ?? 'validation failed'}`, layer };\n }\n }\n // Never echo a secret's value back to stdout/history — confirm with a mask.\n const shown = ctx.dials.sensitivityFor(key) === 'secret' ? '•••• (set)' : JSON.stringify(value);\n return { output: `set ${key} = ${shown}`, layer: { ...layer, [key]: value } };\n }\n case 'unset': {\n const key = positional[0];\n if (!key) return { output: 'usage: unset <key>', layer };\n const next = { ...layer };\n delete next[key];\n return { output: `unset ${key}`, layer: next };\n }\n default:\n return { output: `usage: dials <list|get|set|unset> [...] (got \"${command ?? ''}\")`, layer };\n }\n}\n"],"mappings":"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","names":["import_dials_core"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DialsDefinition, ScopedLayer, Layer, SettingKey, Sensitivity } from '@zodal/dials-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Emit a JSON Schema for a settings surface — point a settings file's `$schema` at it and any editor
|
|
6
|
+
* gives autocomplete + validation (the VS Code `settings.json` experience). The schema describes the
|
|
7
|
+
* FLAT dotted keyspace; `required` is dropped by default (a settings file is a sparse override, every
|
|
8
|
+
* key optional) and secret defaults are redacted (the schema carries structure, not secret values).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface ToJsonSchemaOptions {
|
|
12
|
+
$id?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Allow keys not in the schema. Default: false (flags typos in a settings file). */
|
|
15
|
+
additionalProperties?: boolean;
|
|
16
|
+
/** Keep `required` (default: false — settings overrides are all optional). */
|
|
17
|
+
keepRequired?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Build a JSON Schema describing the settings (for editor autocomplete/validation). */
|
|
20
|
+
declare function toJsonSchema<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options?: ToJsonSchemaOptions): Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Emit an AI/LLM-consumable description of a settings surface — so an assistant can help a user
|
|
24
|
+
* configure the system ("set the editor theme to dark", "what controls connection pooling?"). Lists
|
|
25
|
+
* each setting with its type, allowed values, default, and description. Secrets are marked `[secret]`
|
|
26
|
+
* and never carry their default value.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
interface ToPromptOptions {
|
|
30
|
+
/** A heading for the settings block. Default: 'Settings'. */
|
|
31
|
+
title?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Build an LLM-consumable description of the settings. */
|
|
34
|
+
declare function toPrompt<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options?: ToPromptOptions): string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Headless CLI helpers for zodal-dials — the logic behind a `dials get|set|list|unset` command,
|
|
38
|
+
* pure and IO-free (the consumer wires stores + argv + stdout). `list --show-origin` surfaces the
|
|
39
|
+
* cascade's PROVENANCE per key (the differentiator, à la `git config --show-origin`). Secrets are
|
|
40
|
+
* always shown masked. `set`/`unset` return a NEW editable layer; they never mutate the input.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
interface CliContext<T extends z.ZodObject<z.ZodRawShape>> {
|
|
44
|
+
dials: DialsDefinition<T>;
|
|
45
|
+
/** The ordered scope stack BELOW the editable layer (defaults are prepended by resolve). */
|
|
46
|
+
stack?: ScopedLayer[];
|
|
47
|
+
/** The editable (user) layer being get/set. */
|
|
48
|
+
layer: Layer;
|
|
49
|
+
/** The scope id of the editable layer. Default: 'user'. */
|
|
50
|
+
scope?: string;
|
|
51
|
+
}
|
|
52
|
+
interface RunCliResult {
|
|
53
|
+
output: string;
|
|
54
|
+
/** The (possibly updated) editable layer — changed by `set`/`unset`, unchanged otherwise. */
|
|
55
|
+
layer: Layer;
|
|
56
|
+
}
|
|
57
|
+
/** Coerce a raw CLI string to a value of the given base type. Throws on a malformed number/JSON. */
|
|
58
|
+
declare function coerceByType(type: string, raw: string): unknown;
|
|
59
|
+
interface ListOptions {
|
|
60
|
+
showOrigin?: boolean;
|
|
61
|
+
modifiedOnly?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Format the effective settings as lines (`key = value`, optionally `(scope)`). Secrets masked. */
|
|
64
|
+
declare function formatList<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, options?: ListOptions): string;
|
|
65
|
+
/** Format a single setting's effective value + origin. */
|
|
66
|
+
declare function formatGet<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, key: SettingKey): string;
|
|
67
|
+
/**
|
|
68
|
+
* Dispatch a CLI command against the context. `argv` is the command + args (e.g. `['set',
|
|
69
|
+
* 'editor.fontSize', '16']`). Returns the output text and the (possibly new) editable layer. Pure.
|
|
70
|
+
*/
|
|
71
|
+
declare function runCli<T extends z.ZodObject<z.ZodRawShape>>(argv: string[], ctx: CliContext<T>): RunCliResult;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Internal per-key descriptor for codegen (type, default, enum values, description, sensitivity),
|
|
75
|
+
* derived from a `DialsDefinition` via dials-core introspection + `@zodal/core` enum extraction.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
interface CodegenField {
|
|
79
|
+
key: string;
|
|
80
|
+
type: string;
|
|
81
|
+
default: unknown;
|
|
82
|
+
enumValues?: string[];
|
|
83
|
+
description?: string;
|
|
84
|
+
sensitivity: Sensitivity;
|
|
85
|
+
}
|
|
86
|
+
/** Describe every setting for codegen consumers (schema/prompt/CLI). */
|
|
87
|
+
declare function describeForCodegen<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): CodegenField[];
|
|
88
|
+
|
|
89
|
+
export { type CliContext, type CodegenField, type ListOptions, type RunCliResult, type ToJsonSchemaOptions, type ToPromptOptions, coerceByType, describeForCodegen, formatGet, formatList, runCli, toJsonSchema, toPrompt };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DialsDefinition, ScopedLayer, Layer, SettingKey, Sensitivity } from '@zodal/dials-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Emit a JSON Schema for a settings surface — point a settings file's `$schema` at it and any editor
|
|
6
|
+
* gives autocomplete + validation (the VS Code `settings.json` experience). The schema describes the
|
|
7
|
+
* FLAT dotted keyspace; `required` is dropped by default (a settings file is a sparse override, every
|
|
8
|
+
* key optional) and secret defaults are redacted (the schema carries structure, not secret values).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface ToJsonSchemaOptions {
|
|
12
|
+
$id?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Allow keys not in the schema. Default: false (flags typos in a settings file). */
|
|
15
|
+
additionalProperties?: boolean;
|
|
16
|
+
/** Keep `required` (default: false — settings overrides are all optional). */
|
|
17
|
+
keepRequired?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Build a JSON Schema describing the settings (for editor autocomplete/validation). */
|
|
20
|
+
declare function toJsonSchema<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options?: ToJsonSchemaOptions): Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Emit an AI/LLM-consumable description of a settings surface — so an assistant can help a user
|
|
24
|
+
* configure the system ("set the editor theme to dark", "what controls connection pooling?"). Lists
|
|
25
|
+
* each setting with its type, allowed values, default, and description. Secrets are marked `[secret]`
|
|
26
|
+
* and never carry their default value.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
interface ToPromptOptions {
|
|
30
|
+
/** A heading for the settings block. Default: 'Settings'. */
|
|
31
|
+
title?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Build an LLM-consumable description of the settings. */
|
|
34
|
+
declare function toPrompt<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options?: ToPromptOptions): string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Headless CLI helpers for zodal-dials — the logic behind a `dials get|set|list|unset` command,
|
|
38
|
+
* pure and IO-free (the consumer wires stores + argv + stdout). `list --show-origin` surfaces the
|
|
39
|
+
* cascade's PROVENANCE per key (the differentiator, à la `git config --show-origin`). Secrets are
|
|
40
|
+
* always shown masked. `set`/`unset` return a NEW editable layer; they never mutate the input.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
interface CliContext<T extends z.ZodObject<z.ZodRawShape>> {
|
|
44
|
+
dials: DialsDefinition<T>;
|
|
45
|
+
/** The ordered scope stack BELOW the editable layer (defaults are prepended by resolve). */
|
|
46
|
+
stack?: ScopedLayer[];
|
|
47
|
+
/** The editable (user) layer being get/set. */
|
|
48
|
+
layer: Layer;
|
|
49
|
+
/** The scope id of the editable layer. Default: 'user'. */
|
|
50
|
+
scope?: string;
|
|
51
|
+
}
|
|
52
|
+
interface RunCliResult {
|
|
53
|
+
output: string;
|
|
54
|
+
/** The (possibly updated) editable layer — changed by `set`/`unset`, unchanged otherwise. */
|
|
55
|
+
layer: Layer;
|
|
56
|
+
}
|
|
57
|
+
/** Coerce a raw CLI string to a value of the given base type. Throws on a malformed number/JSON. */
|
|
58
|
+
declare function coerceByType(type: string, raw: string): unknown;
|
|
59
|
+
interface ListOptions {
|
|
60
|
+
showOrigin?: boolean;
|
|
61
|
+
modifiedOnly?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Format the effective settings as lines (`key = value`, optionally `(scope)`). Secrets masked. */
|
|
64
|
+
declare function formatList<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, options?: ListOptions): string;
|
|
65
|
+
/** Format a single setting's effective value + origin. */
|
|
66
|
+
declare function formatGet<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, key: SettingKey): string;
|
|
67
|
+
/**
|
|
68
|
+
* Dispatch a CLI command against the context. `argv` is the command + args (e.g. `['set',
|
|
69
|
+
* 'editor.fontSize', '16']`). Returns the output text and the (possibly new) editable layer. Pure.
|
|
70
|
+
*/
|
|
71
|
+
declare function runCli<T extends z.ZodObject<z.ZodRawShape>>(argv: string[], ctx: CliContext<T>): RunCliResult;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Internal per-key descriptor for codegen (type, default, enum values, description, sensitivity),
|
|
75
|
+
* derived from a `DialsDefinition` via dials-core introspection + `@zodal/core` enum extraction.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
interface CodegenField {
|
|
79
|
+
key: string;
|
|
80
|
+
type: string;
|
|
81
|
+
default: unknown;
|
|
82
|
+
enumValues?: string[];
|
|
83
|
+
description?: string;
|
|
84
|
+
sensitivity: Sensitivity;
|
|
85
|
+
}
|
|
86
|
+
/** Describe every setting for codegen consumers (schema/prompt/CLI). */
|
|
87
|
+
declare function describeForCodegen<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): CodegenField[];
|
|
88
|
+
|
|
89
|
+
export { type CliContext, type CodegenField, type ListOptions, type RunCliResult, type ToJsonSchemaOptions, type ToPromptOptions, coerceByType, describeForCodegen, formatGet, formatList, runCli, toJsonSchema, toPrompt };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// src/json-schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/introspect.ts
|
|
5
|
+
import { baseType, getObjectShape, readMeta } from "@zodal/dials-core";
|
|
6
|
+
import { getEnumValues } from "@zodal/core";
|
|
7
|
+
function safe(fn) {
|
|
8
|
+
try {
|
|
9
|
+
return fn();
|
|
10
|
+
} catch {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function describeForCodegen(dials) {
|
|
15
|
+
const shape = getObjectShape(dials.schema);
|
|
16
|
+
return dials.keys.map((key) => {
|
|
17
|
+
const field = shape[key];
|
|
18
|
+
const meta = field ? readMeta(field) : {};
|
|
19
|
+
const enumValues = field ? safe(() => getEnumValues(field)) : void 0;
|
|
20
|
+
return {
|
|
21
|
+
key,
|
|
22
|
+
type: field ? baseType(field) : "unknown",
|
|
23
|
+
default: dials.defaults[key],
|
|
24
|
+
enumValues: enumValues && enumValues.length > 0 ? enumValues : void 0,
|
|
25
|
+
description: typeof meta.description === "string" ? meta.description : void 0,
|
|
26
|
+
sensitivity: dials.sensitivityFor(key)
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/json-schema.ts
|
|
32
|
+
function fallbackSchema(dials) {
|
|
33
|
+
const properties = {};
|
|
34
|
+
for (const field of describeForCodegen(dials)) {
|
|
35
|
+
const prop = {};
|
|
36
|
+
if (field.type === "number" || field.type === "boolean") prop.type = field.type;
|
|
37
|
+
else if (field.type === "enum") {
|
|
38
|
+
prop.type = "string";
|
|
39
|
+
if (field.enumValues) prop.enum = field.enumValues;
|
|
40
|
+
} else if (field.type === "array") prop.type = "array";
|
|
41
|
+
else if (field.type === "object") prop.type = "object";
|
|
42
|
+
else prop.type = "string";
|
|
43
|
+
if (field.default !== void 0) prop.default = field.default;
|
|
44
|
+
if (field.description) prop.description = field.description;
|
|
45
|
+
properties[field.key] = prop;
|
|
46
|
+
}
|
|
47
|
+
return { $schema: "https://json-schema.org/draft/2020-12/schema", type: "object", properties };
|
|
48
|
+
}
|
|
49
|
+
function toJsonSchema(dials, options = {}) {
|
|
50
|
+
let schema;
|
|
51
|
+
try {
|
|
52
|
+
schema = z.toJSONSchema(dials.schema);
|
|
53
|
+
} catch {
|
|
54
|
+
schema = fallbackSchema(dials);
|
|
55
|
+
}
|
|
56
|
+
if (!options.keepRequired) delete schema.required;
|
|
57
|
+
schema.additionalProperties = options.additionalProperties ?? false;
|
|
58
|
+
if (options.$id) schema.$id = options.$id;
|
|
59
|
+
if (options.title) schema.title = options.title;
|
|
60
|
+
const properties = schema.properties;
|
|
61
|
+
if (properties) {
|
|
62
|
+
for (const key of Object.keys(properties)) {
|
|
63
|
+
if (dials.sensitivityFor(key) === "secret") {
|
|
64
|
+
const prop = properties[key];
|
|
65
|
+
delete prop.default;
|
|
66
|
+
delete prop.const;
|
|
67
|
+
delete prop.enum;
|
|
68
|
+
delete prop.examples;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (schema.additionalProperties === false && !properties["$schema"]) {
|
|
72
|
+
properties["$schema"] = { type: "string" };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return schema;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/prompt.ts
|
|
79
|
+
function toPrompt(dials, options = {}) {
|
|
80
|
+
const fields = describeForCodegen(dials);
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push(`# ${options.title ?? "Settings"}`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push(
|
|
85
|
+
`This system exposes ${fields.length} settings. Each is a typed, named parameter identified by a dotted key. To change one, supply its key and a value of the stated type (for enums, one of the listed values).`
|
|
86
|
+
);
|
|
87
|
+
lines.push("");
|
|
88
|
+
for (const field of fields) {
|
|
89
|
+
let line = `- \`${field.key}\` (${field.type}`;
|
|
90
|
+
if (field.enumValues) line += `: ${field.enumValues.join(" | ")}`;
|
|
91
|
+
if (field.sensitivity !== "secret" && field.default !== void 0) line += `, default ${JSON.stringify(field.default)}`;
|
|
92
|
+
line += ")";
|
|
93
|
+
if (field.sensitivity === "secret") line += " [secret]";
|
|
94
|
+
if (field.description) line += ` \u2014 ${field.description}`;
|
|
95
|
+
lines.push(line);
|
|
96
|
+
}
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/cli.ts
|
|
101
|
+
import { baseType as baseType2, getObjectShape as getObjectShape2, isSecretRef } from "@zodal/dials-core";
|
|
102
|
+
function coerceByType(type, raw) {
|
|
103
|
+
switch (type) {
|
|
104
|
+
case "number": {
|
|
105
|
+
const n = Number(raw);
|
|
106
|
+
if (Number.isNaN(n)) throw new Error(`"${raw}" is not a number`);
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
case "boolean": {
|
|
110
|
+
const t = raw.toLowerCase();
|
|
111
|
+
if (t === "true" || t === "1" || t === "yes") return true;
|
|
112
|
+
if (t === "false" || t === "0" || t === "no") return false;
|
|
113
|
+
throw new Error(`"${raw}" is not a boolean (use true/false)`);
|
|
114
|
+
}
|
|
115
|
+
case "object":
|
|
116
|
+
case "array":
|
|
117
|
+
return JSON.parse(raw);
|
|
118
|
+
case "string":
|
|
119
|
+
case "enum":
|
|
120
|
+
return raw;
|
|
121
|
+
default:
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(raw);
|
|
124
|
+
} catch {
|
|
125
|
+
return raw;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function typeOf(dials, key) {
|
|
130
|
+
const field = getObjectShape2(dials.schema)[key];
|
|
131
|
+
return field ? baseType2(field) : "unknown";
|
|
132
|
+
}
|
|
133
|
+
function formatValue(value) {
|
|
134
|
+
if (isSecretRef(value)) return value.masked;
|
|
135
|
+
return JSON.stringify(value);
|
|
136
|
+
}
|
|
137
|
+
function resolveContext(ctx) {
|
|
138
|
+
const stack = [...ctx.stack ?? [], { scope: ctx.scope ?? "user", layer: ctx.layer }];
|
|
139
|
+
return ctx.dials.resolve(stack, { maskSecrets: true });
|
|
140
|
+
}
|
|
141
|
+
function formatList(ctx, options = {}) {
|
|
142
|
+
const result = resolveContext(ctx);
|
|
143
|
+
const lines = [];
|
|
144
|
+
const extras = Object.keys(result.provenance).filter((k) => !ctx.dials.keys.includes(k));
|
|
145
|
+
for (const key of [...ctx.dials.keys, ...extras]) {
|
|
146
|
+
const prov = result.provenance[key];
|
|
147
|
+
if (!prov) continue;
|
|
148
|
+
if (options.modifiedOnly && prov.winningScope === "default") continue;
|
|
149
|
+
let line = `${key} = ${formatValue(result.effective[key])}`;
|
|
150
|
+
if (options.showOrigin) line += ` (${prov.winningScope}${prov.managed ? ", policy" : ""})`;
|
|
151
|
+
lines.push(line);
|
|
152
|
+
}
|
|
153
|
+
return lines.join("\n");
|
|
154
|
+
}
|
|
155
|
+
function formatGet(ctx, key) {
|
|
156
|
+
if (!ctx.dials.keys.includes(key)) return `unknown setting: ${key}`;
|
|
157
|
+
const result = resolveContext(ctx);
|
|
158
|
+
const prov = result.provenance[key];
|
|
159
|
+
if (!prov) return `${key} is unset`;
|
|
160
|
+
const shadow = prov.shadowed.length > 0 ? ` [shadows ${prov.shadowed.map((s) => s.scope).join(", ")}]` : "";
|
|
161
|
+
return `${key} = ${formatValue(result.effective[key])} (${prov.winningScope}${prov.managed ? ", policy" : ""})${shadow}`;
|
|
162
|
+
}
|
|
163
|
+
function runCli(argv, ctx) {
|
|
164
|
+
const [command, ...rest] = argv;
|
|
165
|
+
const flags = new Set(rest.filter((a) => a.startsWith("--")));
|
|
166
|
+
const positional = rest.filter((a) => !a.startsWith("--"));
|
|
167
|
+
const layer = ctx.layer;
|
|
168
|
+
switch (command) {
|
|
169
|
+
case "list":
|
|
170
|
+
return { output: formatList(ctx, { showOrigin: flags.has("--show-origin"), modifiedOnly: flags.has("--modified") }), layer };
|
|
171
|
+
case "get": {
|
|
172
|
+
const key = positional[0];
|
|
173
|
+
if (!key) return { output: "usage: get <key>", layer };
|
|
174
|
+
return { output: formatGet(ctx, key), layer };
|
|
175
|
+
}
|
|
176
|
+
case "set": {
|
|
177
|
+
const [key, raw] = positional;
|
|
178
|
+
if (key === void 0 || raw === void 0) return { output: "usage: set <key> <value>", layer };
|
|
179
|
+
if (!ctx.dials.keys.includes(key)) return { output: `unknown setting: ${key}`, layer };
|
|
180
|
+
let value;
|
|
181
|
+
try {
|
|
182
|
+
value = coerceByType(typeOf(ctx.dials, key), raw);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
return { output: `invalid value for ${key}: ${error.message}`, layer };
|
|
185
|
+
}
|
|
186
|
+
const field = getObjectShape2(ctx.dials.schema)[key];
|
|
187
|
+
if (field) {
|
|
188
|
+
const parsed = field.safeParse(value);
|
|
189
|
+
if (!parsed.success) {
|
|
190
|
+
return { output: `invalid value for ${key}: ${parsed.error.issues[0]?.message ?? "validation failed"}`, layer };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const shown = ctx.dials.sensitivityFor(key) === "secret" ? "\u2022\u2022\u2022\u2022 (set)" : JSON.stringify(value);
|
|
194
|
+
return { output: `set ${key} = ${shown}`, layer: { ...layer, [key]: value } };
|
|
195
|
+
}
|
|
196
|
+
case "unset": {
|
|
197
|
+
const key = positional[0];
|
|
198
|
+
if (!key) return { output: "usage: unset <key>", layer };
|
|
199
|
+
const next = { ...layer };
|
|
200
|
+
delete next[key];
|
|
201
|
+
return { output: `unset ${key}`, layer: next };
|
|
202
|
+
}
|
|
203
|
+
default:
|
|
204
|
+
return { output: `usage: dials <list|get|set|unset> [...] (got "${command ?? ""}")`, layer };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
coerceByType,
|
|
209
|
+
describeForCodegen,
|
|
210
|
+
formatGet,
|
|
211
|
+
formatList,
|
|
212
|
+
runCli,
|
|
213
|
+
toJsonSchema,
|
|
214
|
+
toPrompt
|
|
215
|
+
};
|
|
216
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/json-schema.ts","../src/introspect.ts","../src/prompt.ts","../src/cli.ts"],"sourcesContent":["/**\n * Emit a JSON Schema for a settings surface — point a settings file's `$schema` at it and any editor\n * gives autocomplete + validation (the VS Code `settings.json` experience). The schema describes the\n * FLAT dotted keyspace; `required` is dropped by default (a settings file is a sparse override, every\n * key optional) and secret defaults are redacted (the schema carries structure, not secret values).\n */\n\nimport { z } from 'zod';\nimport type { DialsDefinition } from '@zodal/dials-core';\nimport { describeForCodegen } from './introspect.js';\n\nexport interface ToJsonSchemaOptions {\n $id?: string;\n title?: string;\n /** Allow keys not in the schema. Default: false (flags typos in a settings file). */\n additionalProperties?: boolean;\n /** Keep `required` (default: false — settings overrides are all optional). */\n keepRequired?: boolean;\n}\n\nfunction fallbackSchema<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): Record<string, unknown> {\n const properties: Record<string, Record<string, unknown>> = {};\n for (const field of describeForCodegen(dials)) {\n const prop: Record<string, unknown> = {};\n if (field.type === 'number' || field.type === 'boolean') prop.type = field.type;\n else if (field.type === 'enum') {\n prop.type = 'string';\n if (field.enumValues) prop.enum = field.enumValues;\n } else if (field.type === 'array') prop.type = 'array';\n else if (field.type === 'object') prop.type = 'object';\n else prop.type = 'string';\n if (field.default !== undefined) prop.default = field.default;\n if (field.description) prop.description = field.description;\n properties[field.key] = prop;\n }\n return { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties };\n}\n\n/** Build a JSON Schema describing the settings (for editor autocomplete/validation). */\nexport function toJsonSchema<T extends z.ZodObject<z.ZodRawShape>>(\n dials: DialsDefinition<T>,\n options: ToJsonSchemaOptions = {},\n): Record<string, unknown> {\n let schema: Record<string, unknown>;\n try {\n // z.toJSONSchema throws on unrepresentable types — fall back to a hand-built schema.\n schema = z.toJSONSchema(dials.schema) as Record<string, unknown>;\n } catch {\n schema = fallbackSchema(dials);\n }\n\n if (!options.keepRequired) delete schema.required;\n schema.additionalProperties = options.additionalProperties ?? false;\n if (options.$id) schema.$id = options.$id;\n if (options.title) schema.title = options.title;\n\n const properties = schema.properties as Record<string, Record<string, unknown>> | undefined;\n if (properties) {\n // Redact ALL value-bearing keywords for secret keys — not just `default`, but `const`/`enum`/\n // `examples` too — so a secret's value (or value set) never appears in the emitted schema.\n for (const key of Object.keys(properties)) {\n if (dials.sensitivityFor(key) === 'secret') {\n const prop = properties[key];\n delete prop.default;\n delete prop.const;\n delete prop.enum;\n delete prop.examples;\n }\n }\n // Allow the conventional `\"$schema\": \"…\"` pointer the settings file carries, so the schema does\n // not reject the very file it is meant to validate under `additionalProperties: false`.\n if (schema.additionalProperties === false && !properties['$schema']) {\n properties['$schema'] = { type: 'string' };\n }\n }\n return schema;\n}\n","/**\n * Internal per-key descriptor for codegen (type, default, enum values, description, sensitivity),\n * derived from a `DialsDefinition` via dials-core introspection + `@zodal/core` enum extraction.\n */\n\nimport type { z } from 'zod';\nimport { baseType, getObjectShape, readMeta } from '@zodal/dials-core';\nimport type { DialsDefinition, Sensitivity } from '@zodal/dials-core';\nimport { getEnumValues } from '@zodal/core';\n\nexport interface CodegenField {\n key: string;\n type: string;\n default: unknown;\n enumValues?: string[];\n description?: string;\n sensitivity: Sensitivity;\n}\n\nfunction safe<R>(fn: () => R): R | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/** Describe every setting for codegen consumers (schema/prompt/CLI). */\nexport function describeForCodegen<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>): CodegenField[] {\n const shape = getObjectShape(dials.schema);\n return dials.keys.map((key) => {\n const field = shape[key];\n const meta = field ? readMeta(field) : {};\n const enumValues = field ? safe(() => getEnumValues(field) as unknown as string[]) : undefined;\n return {\n key,\n type: field ? baseType(field) : 'unknown',\n default: dials.defaults[key],\n enumValues: enumValues && enumValues.length > 0 ? enumValues : undefined,\n description: typeof meta.description === 'string' ? meta.description : undefined,\n sensitivity: dials.sensitivityFor(key),\n };\n });\n}\n","/**\n * Emit an AI/LLM-consumable description of a settings surface — so an assistant can help a user\n * configure the system (\"set the editor theme to dark\", \"what controls connection pooling?\"). Lists\n * each setting with its type, allowed values, default, and description. Secrets are marked `[secret]`\n * and never carry their default value.\n */\n\nimport type { z } from 'zod';\nimport type { DialsDefinition } from '@zodal/dials-core';\nimport { describeForCodegen } from './introspect.js';\n\nexport interface ToPromptOptions {\n /** A heading for the settings block. Default: 'Settings'. */\n title?: string;\n}\n\n/** Build an LLM-consumable description of the settings. */\nexport function toPrompt<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, options: ToPromptOptions = {}): string {\n const fields = describeForCodegen(dials);\n const lines: string[] = [];\n lines.push(`# ${options.title ?? 'Settings'}`);\n lines.push('');\n lines.push(\n `This system exposes ${fields.length} settings. Each is a typed, named parameter identified by a dotted key. ` +\n 'To change one, supply its key and a value of the stated type (for enums, one of the listed values).',\n );\n lines.push('');\n for (const field of fields) {\n let line = `- \\`${field.key}\\` (${field.type}`;\n if (field.enumValues) line += `: ${field.enumValues.join(' | ')}`;\n if (field.sensitivity !== 'secret' && field.default !== undefined) line += `, default ${JSON.stringify(field.default)}`;\n line += ')';\n if (field.sensitivity === 'secret') line += ' [secret]';\n if (field.description) line += ` — ${field.description}`;\n lines.push(line);\n }\n return lines.join('\\n');\n}\n","/**\n * Headless CLI helpers for zodal-dials — the logic behind a `dials get|set|list|unset` command,\n * pure and IO-free (the consumer wires stores + argv + stdout). `list --show-origin` surfaces the\n * cascade's PROVENANCE per key (the differentiator, à la `git config --show-origin`). Secrets are\n * always shown masked. `set`/`unset` return a NEW editable layer; they never mutate the input.\n */\n\nimport type { z } from 'zod';\nimport { baseType, getObjectShape, isSecretRef } from '@zodal/dials-core';\nimport type { DialsDefinition, Layer, ScopedLayer, SettingKey } from '@zodal/dials-core';\n\nexport interface CliContext<T extends z.ZodObject<z.ZodRawShape>> {\n dials: DialsDefinition<T>;\n /** The ordered scope stack BELOW the editable layer (defaults are prepended by resolve). */\n stack?: ScopedLayer[];\n /** The editable (user) layer being get/set. */\n layer: Layer;\n /** The scope id of the editable layer. Default: 'user'. */\n scope?: string;\n}\n\nexport interface RunCliResult {\n output: string;\n /** The (possibly updated) editable layer — changed by `set`/`unset`, unchanged otherwise. */\n layer: Layer;\n}\n\n/** Coerce a raw CLI string to a value of the given base type. Throws on a malformed number/JSON. */\nexport function coerceByType(type: string, raw: string): unknown {\n switch (type) {\n case 'number': {\n const n = Number(raw);\n if (Number.isNaN(n)) throw new Error(`\"${raw}\" is not a number`);\n return n;\n }\n case 'boolean': {\n const t = raw.toLowerCase();\n if (t === 'true' || t === '1' || t === 'yes') return true;\n if (t === 'false' || t === '0' || t === 'no') return false;\n throw new Error(`\"${raw}\" is not a boolean (use true/false)`);\n }\n case 'object':\n case 'array':\n return JSON.parse(raw);\n case 'string':\n case 'enum':\n return raw;\n default:\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n}\n\nfunction typeOf<T extends z.ZodObject<z.ZodRawShape>>(dials: DialsDefinition<T>, key: SettingKey): string {\n const field = getObjectShape(dials.schema)[key];\n return field ? baseType(field) : 'unknown';\n}\n\nfunction formatValue(value: unknown): string {\n if (isSecretRef(value)) return value.masked;\n return JSON.stringify(value);\n}\n\nfunction resolveContext<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>): ReturnType<DialsDefinition<T>['resolve']> {\n const stack: ScopedLayer[] = [...(ctx.stack ?? []), { scope: ctx.scope ?? 'user', layer: ctx.layer }];\n return ctx.dials.resolve(stack, { maskSecrets: true });\n}\n\nexport interface ListOptions {\n showOrigin?: boolean;\n modifiedOnly?: boolean;\n}\n\n/** Format the effective settings as lines (`key = value`, optionally `(scope)`). Secrets masked. */\nexport function formatList<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, options: ListOptions = {}): string {\n const result = resolveContext(ctx);\n const lines: string[] = [];\n // schema keys first, then any ad-hoc (out-of-schema) keys that resolved — so nothing is hidden.\n const extras = Object.keys(result.provenance).filter((k) => !ctx.dials.keys.includes(k));\n for (const key of [...ctx.dials.keys, ...extras]) {\n const prov = result.provenance[key];\n if (!prov) continue; // unset\n if (options.modifiedOnly && prov.winningScope === 'default') continue;\n let line = `${key} = ${formatValue(result.effective[key])}`;\n if (options.showOrigin) line += `\\t(${prov.winningScope}${prov.managed ? ', policy' : ''})`;\n lines.push(line);\n }\n return lines.join('\\n');\n}\n\n/** Format a single setting's effective value + origin. */\nexport function formatGet<T extends z.ZodObject<z.ZodRawShape>>(ctx: CliContext<T>, key: SettingKey): string {\n if (!ctx.dials.keys.includes(key)) return `unknown setting: ${key}`;\n const result = resolveContext(ctx);\n const prov = result.provenance[key];\n if (!prov) return `${key} is unset`;\n const shadow = prov.shadowed.length > 0 ? ` [shadows ${prov.shadowed.map((s) => s.scope).join(', ')}]` : '';\n return `${key} = ${formatValue(result.effective[key])}\\t(${prov.winningScope}${prov.managed ? ', policy' : ''})${shadow}`;\n}\n\n/**\n * Dispatch a CLI command against the context. `argv` is the command + args (e.g. `['set',\n * 'editor.fontSize', '16']`). Returns the output text and the (possibly new) editable layer. Pure.\n */\nexport function runCli<T extends z.ZodObject<z.ZodRawShape>>(argv: string[], ctx: CliContext<T>): RunCliResult {\n const [command, ...rest] = argv;\n const flags = new Set(rest.filter((a) => a.startsWith('--')));\n const positional = rest.filter((a) => !a.startsWith('--'));\n const layer = ctx.layer;\n\n switch (command) {\n case 'list':\n return { output: formatList(ctx, { showOrigin: flags.has('--show-origin'), modifiedOnly: flags.has('--modified') }), layer };\n case 'get': {\n const key = positional[0];\n if (!key) return { output: 'usage: get <key>', layer };\n return { output: formatGet(ctx, key), layer };\n }\n case 'set': {\n const [key, raw] = positional;\n if (key === undefined || raw === undefined) return { output: 'usage: set <key> <value>', layer };\n if (!ctx.dials.keys.includes(key)) return { output: `unknown setting: ${key}`, layer };\n let value: unknown;\n try {\n value = coerceByType(typeOf(ctx.dials, key), raw);\n } catch (error) {\n return { output: `invalid value for ${key}: ${(error as Error).message}`, layer };\n }\n // Validate the coerced value against the field schema (rejects out-of-enum / out-of-range).\n const field = getObjectShape(ctx.dials.schema)[key];\n if (field) {\n const parsed = field.safeParse(value);\n if (!parsed.success) {\n return { output: `invalid value for ${key}: ${parsed.error.issues[0]?.message ?? 'validation failed'}`, layer };\n }\n }\n // Never echo a secret's value back to stdout/history — confirm with a mask.\n const shown = ctx.dials.sensitivityFor(key) === 'secret' ? '•••• (set)' : JSON.stringify(value);\n return { output: `set ${key} = ${shown}`, layer: { ...layer, [key]: value } };\n }\n case 'unset': {\n const key = positional[0];\n if (!key) return { output: 'usage: unset <key>', layer };\n const next = { ...layer };\n delete next[key];\n return { output: `unset ${key}`, layer: next };\n }\n default:\n return { output: `usage: dials <list|get|set|unset> [...] (got \"${command ?? ''}\")`, layer };\n }\n}\n"],"mappings":"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","names":["baseType","getObjectShape"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zodal/dials-codegen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Machine-interface emit for zodal-dials: JSON Schema (editor autocomplete), toPrompt (AI), and CLI helpers (get/set/list --show-origin)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@zodal/core": "^0.1.2",
|
|
29
|
+
"@zodal/dials-core": "^0.1.0",
|
|
30
|
+
"zod": ">=4.1.13"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@zodal/core": "^0.1.2",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vitest": "^3.0.0",
|
|
37
|
+
"zod": "^4.4.0",
|
|
38
|
+
"@zodal/dials-core": "0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|