norn-cli 1.10.5 → 1.11.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/.kanbn/index.md +30 -0
- package/.kanbn/tasks/book-first-mentor-session.md +13 -0
- package/.kanbn/tasks/decide-what-success-in-a-pilot-looks-like.md +9 -0
- package/.kanbn/tasks/do-5-customer-conversations.md +9 -0
- package/.kanbn/tasks/finalise-the-one-line-pitch.md +11 -0
- package/.kanbn/tasks/interview-script.md +49 -0
- package/.kanbn/tasks/make-a-list-of-10-people-to-speak-to.md +11 -0
- package/.kanbn/tasks/prepare-your-customer-interview-questions.md +11 -0
- package/.kanbn/tasks/recruit-2/342/200/2233-pilot-users.md +9 -0
- package/.kanbn/tasks/refine-your-pitch.md +9 -0
- package/.kanbn/tasks/write-down-repeated-wording.md +9 -0
- package/.kanbn/tasks/write-the-one-pager.md +27 -0
- package/CHANGELOG.md +34 -0
- package/README.md +75 -1132
- package/dist/cli.js +20855 -3573
- package/package.json +36 -1
- package/schemas/norn.mcp.schema.json +125 -0
- package/scripts/generate-coding-bed.mjs +243 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn - REST Client",
|
|
4
4
|
"description": "A powerful REST client for making HTTP requests with sequences, variables, scripts, and cookie support",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.11.0",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|
|
@@ -103,6 +103,16 @@
|
|
|
103
103
|
"title": "Import Postman Environment JSON",
|
|
104
104
|
"category": "Norn"
|
|
105
105
|
},
|
|
106
|
+
{
|
|
107
|
+
"command": "norn.createSqlStarterFiles",
|
|
108
|
+
"title": "Create SQL Starter Files",
|
|
109
|
+
"category": "Norn"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"command": "norn.createMcpStarterConfig",
|
|
113
|
+
"title": "Create MCP Starter Config",
|
|
114
|
+
"category": "Norn"
|
|
115
|
+
},
|
|
106
116
|
{
|
|
107
117
|
"command": "norn.encryptSecretAtLine",
|
|
108
118
|
"title": "Encrypt Secret At Line",
|
|
@@ -152,6 +162,20 @@
|
|
|
152
162
|
}
|
|
153
163
|
]
|
|
154
164
|
},
|
|
165
|
+
"menus": {
|
|
166
|
+
"explorer/context": [
|
|
167
|
+
{
|
|
168
|
+
"command": "norn.createSqlStarterFiles",
|
|
169
|
+
"when": "explorerResourceIsFolder",
|
|
170
|
+
"group": "norn@1"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"command": "norn.createMcpStarterConfig",
|
|
174
|
+
"when": "explorerResourceIsFolder",
|
|
175
|
+
"group": "norn@2"
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
},
|
|
155
179
|
"languages": [
|
|
156
180
|
{
|
|
157
181
|
"id": "norn",
|
|
@@ -238,6 +262,15 @@
|
|
|
238
262
|
"path": "./syntaxes/nornapi.tmLanguage.json"
|
|
239
263
|
}
|
|
240
264
|
],
|
|
265
|
+
"jsonValidation": [
|
|
266
|
+
{
|
|
267
|
+
"fileMatch": [
|
|
268
|
+
"norn.mcp.json",
|
|
269
|
+
"/norn.mcp.json"
|
|
270
|
+
],
|
|
271
|
+
"url": "./schemas/norn.mcp.schema.json"
|
|
272
|
+
}
|
|
273
|
+
],
|
|
241
274
|
"breakpoints": [
|
|
242
275
|
{
|
|
243
276
|
"language": "norn"
|
|
@@ -409,10 +442,12 @@
|
|
|
409
442
|
"typescript-eslint": "^8.52.0"
|
|
410
443
|
},
|
|
411
444
|
"dependencies": {
|
|
445
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
412
446
|
"ajv": "^8.17.1",
|
|
413
447
|
"axios": "^1.13.2",
|
|
414
448
|
"chalk": "^5.4.1",
|
|
415
449
|
"mssql": "^12.2.0",
|
|
450
|
+
"norn-cli": "^1.10.6",
|
|
416
451
|
"pg": "^8.20.0",
|
|
417
452
|
"tough-cookie": "^6.0.0"
|
|
418
453
|
},
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "norn.mcp.schema.json",
|
|
4
|
+
"title": "Norn MCP Config",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"version",
|
|
9
|
+
"servers"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"_comment": {
|
|
13
|
+
"oneOf": [
|
|
14
|
+
{
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"description": "Optional human-readable guidance ignored by Norn at runtime."
|
|
25
|
+
},
|
|
26
|
+
"version": {
|
|
27
|
+
"type": "integer",
|
|
28
|
+
"const": 1,
|
|
29
|
+
"description": "Config schema version."
|
|
30
|
+
},
|
|
31
|
+
"servers": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "Named MCP server aliases available to run mcp list/call.",
|
|
34
|
+
"minProperties": 1,
|
|
35
|
+
"additionalProperties": {
|
|
36
|
+
"oneOf": [
|
|
37
|
+
{
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": [
|
|
41
|
+
"transport",
|
|
42
|
+
"command"
|
|
43
|
+
],
|
|
44
|
+
"properties": {
|
|
45
|
+
"_comment": {
|
|
46
|
+
"oneOf": [
|
|
47
|
+
{
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "string"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"description": "Optional human-readable guidance ignored by Norn at runtime."
|
|
58
|
+
},
|
|
59
|
+
"transport": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"const": "stdio"
|
|
62
|
+
},
|
|
63
|
+
"command": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"minItems": 1,
|
|
66
|
+
"items": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"description": "Command and arguments used to start the MCP server. Relative paths resolve from this config file."
|
|
70
|
+
},
|
|
71
|
+
"cwd": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Optional working directory for the stdio command. Relative paths resolve from this config file."
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"type": "object",
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"required": [
|
|
81
|
+
"transport",
|
|
82
|
+
"url"
|
|
83
|
+
],
|
|
84
|
+
"properties": {
|
|
85
|
+
"_comment": {
|
|
86
|
+
"oneOf": [
|
|
87
|
+
{
|
|
88
|
+
"type": "string"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"description": "Optional human-readable guidance ignored by Norn at runtime."
|
|
98
|
+
},
|
|
99
|
+
"transport": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"const": "http"
|
|
102
|
+
},
|
|
103
|
+
"url": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Streamable HTTP MCP endpoint URL."
|
|
106
|
+
},
|
|
107
|
+
"headers": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"description": "Optional static HTTP headers to send with each MCP request.",
|
|
110
|
+
"additionalProperties": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"timeoutMs": {
|
|
115
|
+
"type": "number",
|
|
116
|
+
"exclusiveMinimum": 0,
|
|
117
|
+
"description": "Optional per-request timeout for initialize, list, and call operations."
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
function getArg(flag, fallback) {
|
|
9
|
+
const index = args.indexOf(flag);
|
|
10
|
+
if (index === -1 || index === args.length - 1) {
|
|
11
|
+
return fallback;
|
|
12
|
+
}
|
|
13
|
+
return args[index + 1];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const seconds = Number(getArg('--seconds', '10'));
|
|
17
|
+
const outPath = path.resolve(getArg('--out', 'audio/norn-coding-bed-test.wav'));
|
|
18
|
+
const sampleRate = 44100;
|
|
19
|
+
const bpm = 122;
|
|
20
|
+
const beatLength = 60 / bpm;
|
|
21
|
+
const barLength = beatLength * 4;
|
|
22
|
+
const totalSamples = Math.floor(seconds * sampleRate);
|
|
23
|
+
const left = new Float32Array(totalSamples);
|
|
24
|
+
const right = new Float32Array(totalSamples);
|
|
25
|
+
|
|
26
|
+
const progression = [
|
|
27
|
+
{ root: 45, chord: [57, 60, 64], accent: [69, 72, 76] }, // Am
|
|
28
|
+
{ root: 41, chord: [53, 57, 60], accent: [65, 69, 72] }, // F
|
|
29
|
+
{ root: 36, chord: [48, 52, 55], accent: [64, 67, 72] }, // C
|
|
30
|
+
{ root: 43, chord: [55, 59, 62], accent: [67, 71, 74] }, // G
|
|
31
|
+
{ root: 45, chord: [57, 60, 64], accent: [69, 72, 76] }, // Am
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
let noiseSeed = 0x12345678;
|
|
35
|
+
|
|
36
|
+
function nextNoise() {
|
|
37
|
+
noiseSeed = (1664525 * noiseSeed + 1013904223) >>> 0;
|
|
38
|
+
return (noiseSeed / 0xffffffff) * 2 - 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function midiToHz(note) {
|
|
42
|
+
return 440 * Math.pow(2, (note - 69) / 12);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function clamp(value) {
|
|
46
|
+
return Math.max(-1, Math.min(1, value));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function softClip(value) {
|
|
50
|
+
return Math.tanh(value * 1.4);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function oscSine(freq, time) {
|
|
54
|
+
return Math.sin(2 * Math.PI * freq * time);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function oscTri(freq, time) {
|
|
58
|
+
return (2 / Math.PI) * Math.asin(Math.sin(2 * Math.PI * freq * time));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function oscSaw(freq, time) {
|
|
62
|
+
const phase = freq * time;
|
|
63
|
+
return 2 * (phase - Math.floor(phase + 0.5));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function expEnv(timeSince, decay) {
|
|
67
|
+
return timeSince < 0 ? 0 : Math.exp(-timeSince * decay);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function kickAt(timeSince) {
|
|
71
|
+
if (timeSince < 0 || timeSince > 0.24) {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const pitch = 132 - 78 * Math.min(timeSince / 0.08, 1);
|
|
76
|
+
const body = oscSine(pitch, timeSince) * expEnv(timeSince, 17);
|
|
77
|
+
const click = oscTri(1900, timeSince) * expEnv(timeSince, 95) * 0.18;
|
|
78
|
+
return body * 0.95 + click;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function snareAt(timeSince) {
|
|
82
|
+
if (timeSince < 0 || timeSince > 0.22) {
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const noise = nextNoise() * expEnv(timeSince, 22);
|
|
87
|
+
const body = oscSine(198, timeSince) * expEnv(timeSince, 28) * 0.22;
|
|
88
|
+
return noise * 0.34 + body;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function hatAt(timeSince, open) {
|
|
92
|
+
const length = open ? 0.16 : 0.06;
|
|
93
|
+
if (timeSince < 0 || timeSince > length) {
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const decay = open ? 26 : 54;
|
|
98
|
+
const raw = nextNoise();
|
|
99
|
+
const brightness = oscSine(9200, timeSince) * 0.08 + oscSine(6800, timeSince) * 0.06;
|
|
100
|
+
return (raw * 0.17 + brightness) * expEnv(timeSince, decay);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sidechainAmount(time) {
|
|
104
|
+
const phase = time % beatLength;
|
|
105
|
+
return 1 - 0.28 * Math.exp(-phase * 15);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function bassNoteForStep(barIndex, stepInBar) {
|
|
109
|
+
const current = progression[barIndex % progression.length];
|
|
110
|
+
const root = current.root;
|
|
111
|
+
const fifth = root + 7;
|
|
112
|
+
const octave = root + 12;
|
|
113
|
+
const pattern = [root, root, fifth, root, octave, root, fifth, root];
|
|
114
|
+
return midiToHz(pattern[stepInBar % pattern.length]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function arpNoteForStep(barIndex, stepInBar) {
|
|
118
|
+
const current = progression[barIndex % progression.length];
|
|
119
|
+
const pattern = [0, 1, 2, 1, 2, 1, 0, 2];
|
|
120
|
+
return midiToHz(current.accent[pattern[stepInBar % pattern.length]]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (let sample = 0; sample < totalSamples; sample += 1) {
|
|
124
|
+
const time = sample / sampleRate;
|
|
125
|
+
const barIndex = Math.floor(time / barLength) % progression.length;
|
|
126
|
+
const barTime = time % barLength;
|
|
127
|
+
const current = progression[barIndex];
|
|
128
|
+
|
|
129
|
+
let mixL = 0;
|
|
130
|
+
let mixR = 0;
|
|
131
|
+
|
|
132
|
+
// Pad chord.
|
|
133
|
+
let pad = 0;
|
|
134
|
+
for (const note of current.chord) {
|
|
135
|
+
const freq = midiToHz(note);
|
|
136
|
+
pad += oscSine(freq, time) * 0.12;
|
|
137
|
+
pad += oscSine(freq * 0.5, time) * 0.035;
|
|
138
|
+
}
|
|
139
|
+
pad += oscSine(midiToHz(current.root), time) * 0.03;
|
|
140
|
+
pad *= sidechainAmount(time) * 0.7;
|
|
141
|
+
mixL += pad;
|
|
142
|
+
mixR += pad;
|
|
143
|
+
|
|
144
|
+
// Bass pulse on eighth notes.
|
|
145
|
+
const bassStepLength = beatLength / 2;
|
|
146
|
+
const bassStep = Math.floor(barTime / bassStepLength);
|
|
147
|
+
const bassStepStart = bassStep * bassStepLength;
|
|
148
|
+
const bassTimeSince = barTime - bassStepStart;
|
|
149
|
+
const bassGate = bassTimeSince < 0.21 ? 1 : 0;
|
|
150
|
+
const bassFreq = bassNoteForStep(barIndex, bassStep);
|
|
151
|
+
const bassEnv = expEnv(bassTimeSince, 9) * bassGate;
|
|
152
|
+
const bass =
|
|
153
|
+
(oscTri(bassFreq, time) * 0.22 + oscSine(bassFreq * 0.5, time) * 0.1) *
|
|
154
|
+
bassEnv *
|
|
155
|
+
sidechainAmount(time);
|
|
156
|
+
mixL += bass;
|
|
157
|
+
mixR += bass;
|
|
158
|
+
|
|
159
|
+
// Bright arpeggio.
|
|
160
|
+
const arpStepLength = beatLength / 2;
|
|
161
|
+
const arpStep = Math.floor(barTime / arpStepLength);
|
|
162
|
+
const arpStepStart = arpStep * arpStepLength;
|
|
163
|
+
const arpTimeSince = barTime - arpStepStart;
|
|
164
|
+
const arpFreq = arpNoteForStep(barIndex, arpStep);
|
|
165
|
+
const arpEnv = Math.min(1, arpTimeSince * 40) * expEnv(arpTimeSince, 10);
|
|
166
|
+
const arp =
|
|
167
|
+
(oscSaw(arpFreq, time) * 0.07 + oscSine(arpFreq * 2, time) * 0.05 + oscSine(arpFreq, time) * 0.04) *
|
|
168
|
+
arpEnv;
|
|
169
|
+
const arpPan = arpStep % 2 === 0 ? -0.22 : 0.22;
|
|
170
|
+
mixL += arp * (1 - arpPan);
|
|
171
|
+
mixR += arp * (1 + arpPan);
|
|
172
|
+
|
|
173
|
+
// Kick every beat with an extra pickup before the fourth beat.
|
|
174
|
+
const kickPattern = [0, 1, 2, 2.75, 3];
|
|
175
|
+
let kick = 0;
|
|
176
|
+
for (const beat of kickPattern) {
|
|
177
|
+
kick += kickAt(barTime - beat * beatLength);
|
|
178
|
+
}
|
|
179
|
+
mixL += kick * 0.92;
|
|
180
|
+
mixR += kick * 0.92;
|
|
181
|
+
|
|
182
|
+
// Snare on beats 2 and 4.
|
|
183
|
+
let snare = 0;
|
|
184
|
+
for (const beat of [1, 3]) {
|
|
185
|
+
snare += snareAt(barTime - beat * beatLength);
|
|
186
|
+
}
|
|
187
|
+
mixL += snare * 0.75;
|
|
188
|
+
mixR += snare * 0.75;
|
|
189
|
+
|
|
190
|
+
// Hats on offbeats with occasional longer accent.
|
|
191
|
+
let hat = 0;
|
|
192
|
+
for (let step = 0; step < 8; step += 1) {
|
|
193
|
+
const start = step * bassStepLength;
|
|
194
|
+
const open = step === 3 || step === 7;
|
|
195
|
+
hat += hatAt(barTime - start, open);
|
|
196
|
+
}
|
|
197
|
+
mixL += hat * 0.4;
|
|
198
|
+
mixR += hat * 0.38;
|
|
199
|
+
|
|
200
|
+
// Gentle master fade in/out.
|
|
201
|
+
const fadeIn = Math.min(1, time / 0.35);
|
|
202
|
+
const fadeOut = Math.min(1, (seconds - time) / 0.5);
|
|
203
|
+
const masterEnv = Math.max(0, Math.min(fadeIn, fadeOut));
|
|
204
|
+
|
|
205
|
+
left[sample] = softClip(mixL * masterEnv);
|
|
206
|
+
right[sample] = softClip(mixR * masterEnv);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let peak = 0;
|
|
210
|
+
for (let i = 0; i < totalSamples; i += 1) {
|
|
211
|
+
peak = Math.max(peak, Math.abs(left[i]), Math.abs(right[i]));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const targetPeak = 0.92;
|
|
215
|
+
const gain = peak > 0 ? targetPeak / peak : 1;
|
|
216
|
+
|
|
217
|
+
const pcmData = Buffer.alloc(totalSamples * 4);
|
|
218
|
+
for (let i = 0; i < totalSamples; i += 1) {
|
|
219
|
+
const l = Math.round(clamp(left[i] * gain) * 32767);
|
|
220
|
+
const r = Math.round(clamp(right[i] * gain) * 32767);
|
|
221
|
+
pcmData.writeInt16LE(l, i * 4);
|
|
222
|
+
pcmData.writeInt16LE(r, i * 4 + 2);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const header = Buffer.alloc(44);
|
|
226
|
+
header.write('RIFF', 0);
|
|
227
|
+
header.writeUInt32LE(36 + pcmData.length, 4);
|
|
228
|
+
header.write('WAVE', 8);
|
|
229
|
+
header.write('fmt ', 12);
|
|
230
|
+
header.writeUInt32LE(16, 16);
|
|
231
|
+
header.writeUInt16LE(1, 20);
|
|
232
|
+
header.writeUInt16LE(2, 22);
|
|
233
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
234
|
+
header.writeUInt32LE(sampleRate * 4, 28);
|
|
235
|
+
header.writeUInt16LE(4, 32);
|
|
236
|
+
header.writeUInt16LE(16, 34);
|
|
237
|
+
header.write('data', 36);
|
|
238
|
+
header.writeUInt32LE(pcmData.length, 40);
|
|
239
|
+
|
|
240
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
241
|
+
fs.writeFileSync(outPath, Buffer.concat([header, pcmData]));
|
|
242
|
+
|
|
243
|
+
console.log(`Wrote ${outPath}`);
|