norn-cli 3.3.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1396 -276
- package/dist/knowledgeIndexWorker.js +71 -35
- package/package.json +3 -3
- package/schemas/norn.config.schema.json +13 -0
- package/CLAUDE.md +0 -69
- package/NOW.md +0 -223
- package/demos/agent-workbench/README.md +0 -156
- package/demos/agent-workbench/agents.nornagent +0 -87
- package/demos/agent-workbench/contracts/domain-answer.schema.json +0 -28
- package/demos/agent-workbench/contracts/domain-question.schema.json +0 -34
- package/demos/agent-workbench/contracts/router-verdict.schema.json +0 -43
- package/demos/agent-workbench/contracts/ticket.schema.json +0 -29
- package/demos/agent-workbench/contracts/verdict.schema.json +0 -48
- package/demos/agent-workbench/fake-openai-server.js +0 -128
- package/demos/agent-workbench/fixtures/aligned-ticket.json +0 -10
- package/demos/agent-workbench/fixtures/cross-domain-ticket.json +0 -10
- package/demos/agent-workbench/fixtures/invalid-output-ticket.json +0 -8
- package/demos/agent-workbench/norn.config.json +0 -15
- package/demos/agent-workbench/prompts/ticket-router.md +0 -11
- package/demos/agent-workbench/workbench.norn +0 -32
- package/demos/knowledge-editor/README.md +0 -74
- package/demos/knowledge-editor/agents.nornagent +0 -16
- package/demos/knowledge-editor/api.nornapi +0 -10
- package/demos/knowledge-editor/handbook/ops/flow.svg +0 -1
- package/demos/knowledge-editor/handbook/ops/retired.md +0 -9
- package/demos/knowledge-editor/handbook/ops/runbook.md +0 -14
- package/demos/knowledge-editor/handbook/payments/charges.md +0 -25
- package/demos/knowledge-editor/handbook/payments/pricing-tiers.draft.md +0 -7
- package/demos/knowledge-editor/handbook/payments/refunds/policy.md +0 -12
- package/demos/knowledge-editor/support.norn +0 -10
- package/demos/mcp-ticket-testing/README.md +0 -114
- package/demos/mcp-ticket-testing/agents.nornagent +0 -77
- package/demos/mcp-ticket-testing/contracts/test-run.schema.json +0 -31
- package/demos/mcp-ticket-testing/expectations/proj-142.md +0 -12
- package/demos/mcp-ticket-testing/fixtures/proj-142.json +0 -13
- package/demos/mcp-ticket-testing/prompts/backend-tester.md +0 -12
- package/demos/mcp-ticket-testing/prompts/frontend-tester.md +0 -14
- package/demos/mcp-ticket-testing/prompts/reporter.md +0 -10
- package/demos/mcp-ticket-testing/servers/browser-server.js +0 -133
- package/demos/mcp-ticket-testing/servers/house-server.js +0 -125
- package/demos/mcp-ticket-testing/tickets.norn +0 -32
- package/demos/nornenv-region-refactor/README.md +0 -64
- package/demos/nornenv-showcase/README.md +0 -62
- package/demos/nornenv-showcase/norn.config.json +0 -16
- package/demos/nornenv-showcase/showcase.norn +0 -70
- package/demos/nornenv-showcase/showcase.nornapi +0 -26
- package/demos/nornenv-showcase/showcase.nornsql +0 -20
- package/demos/tests-showcase/01-single-requests.norn +0 -31
- package/demos/tests-showcase/02-sequences.norn +0 -54
- package/demos/tests-showcase/03-sidecars.norn +0 -42
- package/demos/tests-showcase/04-api-plus-sql.norn +0 -27
- package/demos/tests-showcase/db/testDb.nornsql +0 -12
- package/demos/tests-showcase/demo-api.nornapi +0 -17
- package/demos/tests-showcase/norn.config.json +0 -16
- package/playground/ai.norn +0 -15
- package/playground/ai_orchastration.nornagent +0 -34
- package/playground/knowedge_base/ACDC/testing_notes_new_clients.md +0 -11
- package/playground/knowedge_base/nexus_system_prompt.md +0 -1
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* "House" — the custom MCP server a team writes for its own product.
|
|
5
|
-
*
|
|
6
|
-
* Deterministic on purpose: the demo is about declaring a server and granting it, not about
|
|
7
|
-
* what any particular tool does. Everything here is in-memory and reset per process.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
11
|
-
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
12
|
-
const { ListToolsRequestSchema, CallToolRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
|
|
13
|
-
|
|
14
|
-
const state = {
|
|
15
|
-
tenant: 'acme',
|
|
16
|
-
auditLog: [],
|
|
17
|
-
orders: [
|
|
18
|
-
{ id: 'A-1001', tenant: 'acme', total: 42.5, status: 'placed' },
|
|
19
|
-
{ id: 'A-1002', tenant: 'acme', total: 18.0, status: 'shipped' }
|
|
20
|
-
]
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const TOOLS = [
|
|
24
|
-
{
|
|
25
|
-
name: 'getFixtureUser',
|
|
26
|
-
description: 'Return the seeded test user for the current tenant.',
|
|
27
|
-
inputSchema: { type: 'object', additionalProperties: false, properties: {} }
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'resetTenant',
|
|
31
|
-
description: 'Reset the tenant to its seeded state before a test run.',
|
|
32
|
-
inputSchema: { type: 'object', additionalProperties: false, properties: {} }
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'callEndpoint',
|
|
36
|
-
description: 'Call a backend endpoint and return its status and body.',
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: 'object',
|
|
39
|
-
additionalProperties: false,
|
|
40
|
-
properties: {
|
|
41
|
-
method: { type: 'string', description: 'HTTP method, e.g. GET or POST.' },
|
|
42
|
-
path: { type: 'string', description: 'Endpoint path, e.g. /orders/A-1001.' }
|
|
43
|
-
},
|
|
44
|
-
required: ['method', 'path']
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'readAuditLog',
|
|
49
|
-
description: 'Return the audit entries written during this run, oldest first.',
|
|
50
|
-
inputSchema: { type: 'object', additionalProperties: false, properties: {} }
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'queryDb',
|
|
54
|
-
description: 'Read rows from a seeded table by name.',
|
|
55
|
-
inputSchema: {
|
|
56
|
-
type: 'object',
|
|
57
|
-
additionalProperties: false,
|
|
58
|
-
properties: { table: { type: 'string', description: 'Table to read, e.g. orders.' } },
|
|
59
|
-
required: ['table']
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
function structured(value) {
|
|
65
|
-
return { content: [{ type: 'text', text: JSON.stringify(value) }], structuredContent: value };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function callEndpoint(args) {
|
|
69
|
-
const path = String(args.path || '');
|
|
70
|
-
const method = String(args.method || 'GET').toUpperCase();
|
|
71
|
-
state.auditLog.push({ at: state.auditLog.length + 1, method, path });
|
|
72
|
-
|
|
73
|
-
const orderMatch = path.match(/^\/orders\/([A-Za-z0-9-]+)$/);
|
|
74
|
-
if (orderMatch) {
|
|
75
|
-
const order = state.orders.find(item => item.id === orderMatch[1]);
|
|
76
|
-
return order
|
|
77
|
-
? { status: 200, body: order }
|
|
78
|
-
: { status: 404, body: { error: 'order not found' } };
|
|
79
|
-
}
|
|
80
|
-
if (path === '/orders') {
|
|
81
|
-
return { status: 200, body: { orders: state.orders } };
|
|
82
|
-
}
|
|
83
|
-
return { status: 404, body: { error: 'no such endpoint' } };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function main() {
|
|
87
|
-
const server = new Server(
|
|
88
|
-
{ name: 'norn-demo-house', version: '1.0.0' },
|
|
89
|
-
{ capabilities: { tools: {} } }
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
93
|
-
|
|
94
|
-
server.setRequestHandler(CallToolRequestSchema, async request => {
|
|
95
|
-
const args = request.params.arguments || {};
|
|
96
|
-
switch (request.params.name) {
|
|
97
|
-
case 'getFixtureUser':
|
|
98
|
-
return structured({ id: 'u-1', email: 'tester@example.com', tenant: state.tenant });
|
|
99
|
-
case 'resetTenant':
|
|
100
|
-
state.auditLog = [];
|
|
101
|
-
return structured({ tenant: state.tenant, reset: true });
|
|
102
|
-
case 'callEndpoint':
|
|
103
|
-
return structured(callEndpoint(args));
|
|
104
|
-
case 'readAuditLog':
|
|
105
|
-
return structured({ entries: state.auditLog });
|
|
106
|
-
case 'queryDb':
|
|
107
|
-
return structured({
|
|
108
|
-
table: args.table,
|
|
109
|
-
rows: args.table === 'orders' ? state.orders : []
|
|
110
|
-
});
|
|
111
|
-
default:
|
|
112
|
-
return {
|
|
113
|
-
isError: true,
|
|
114
|
-
content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }]
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
await server.connect(new StdioServerTransport());
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
main().catch(error => {
|
|
123
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
124
|
-
process.exit(1);
|
|
125
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import "./agents.nornagent"
|
|
2
|
-
|
|
3
|
-
# Prove both servers are reachable without spending anything on a model.
|
|
4
|
-
# These steps resolve the same aliases the agents use, so a failure here points at the server
|
|
5
|
-
# rather than at an agent — this is the loop to develop a server against.
|
|
6
|
-
test sequence ServersUp
|
|
7
|
-
var browserTools = run mcp list Browser
|
|
8
|
-
assert browserTools.length > 0
|
|
9
|
-
|
|
10
|
-
var houseTools = run mcp list House
|
|
11
|
-
assert houseTools.length > 0
|
|
12
|
-
|
|
13
|
-
var user = run mcp call House getFixtureUser()
|
|
14
|
-
assert user.structuredContent.email == "tester@example.com"
|
|
15
|
-
end sequence
|
|
16
|
-
|
|
17
|
-
# The application this whole slice was designed against: a ticket arrives, two agents test it
|
|
18
|
-
# from different sides with different tools, a third writes it up, and a judge rules on whether
|
|
19
|
-
# the expected test cases were actually covered.
|
|
20
|
-
test sequence TicketPROJ142
|
|
21
|
-
var ticket = run readJson "./fixtures/proj-142.json"
|
|
22
|
-
|
|
23
|
-
var frontend = run FrontendTester ticket
|
|
24
|
-
var backend = run BackendTester ticket
|
|
25
|
-
# An agent's input is a variable or a string — there is no object-literal form — so the two
|
|
26
|
-
# runs are interpolated into one prompt.
|
|
27
|
-
var report = run Reporter "Frontend run:\n{{frontend.body}}\n\nBackend run:\n{{backend.body}}"
|
|
28
|
-
|
|
29
|
-
print "QA report" | "{{report.text}}"
|
|
30
|
-
|
|
31
|
-
judge report with Reviewer expects file expectations/proj-142.md
|
|
32
|
-
end sequence
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Region-pattern refactor showcase
|
|
2
|
-
|
|
3
|
-
A flat `[env:STAGE_REGION]` matrix that the Norn extension can refactor into the templates + extends shape in one click.
|
|
4
|
-
|
|
5
|
-
## How to use
|
|
6
|
-
|
|
7
|
-
1. Open [.nornenv](./.nornenv) in VS Code.
|
|
8
|
-
2. A `$(sparkle) Refactor 4 envs into 2+2 templates` CodeLens appears at the top of the file.
|
|
9
|
-
3. Click it. A confirmation dialog summarises what will change:
|
|
10
|
-
- 2 vars (`baseUrl`, `apiKey`) lifted to stage templates (`dev`, `prod`)
|
|
11
|
-
- 2 vars (`dbHost`, `bucket`) lifted to region templates (`us`, `uk`)
|
|
12
|
-
- 1 var (`failoverHost`) kept as leaf-specific on `prod_uk`
|
|
13
|
-
4. Confirm. The flat 4-env matrix is replaced with templates + extending envs:
|
|
14
|
-
|
|
15
|
-
```nornenv
|
|
16
|
-
[template:dev]
|
|
17
|
-
var baseUrl = https://dev.example.com
|
|
18
|
-
var apiKey = dev-key-123
|
|
19
|
-
|
|
20
|
-
[template:prod]
|
|
21
|
-
var baseUrl = https://api.example.com
|
|
22
|
-
secret apiKey = prod-key-789
|
|
23
|
-
|
|
24
|
-
[template:us]
|
|
25
|
-
var dbHost = db.us.example.com
|
|
26
|
-
var bucket = data-us
|
|
27
|
-
|
|
28
|
-
[template:uk]
|
|
29
|
-
var dbHost = db.uk.example.com
|
|
30
|
-
var bucket = data-uk
|
|
31
|
-
|
|
32
|
-
[env:dev_us extends dev, us]
|
|
33
|
-
|
|
34
|
-
[env:dev_uk extends dev, uk]
|
|
35
|
-
|
|
36
|
-
[env:prod_us extends prod, us]
|
|
37
|
-
|
|
38
|
-
[env:prod_uk extends prod, uk]
|
|
39
|
-
var failoverHost = api-failover.uk.example.com
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
5. The `secret` keyword on `apiKey` is preserved when the var is lifted to a template.
|
|
43
|
-
|
|
44
|
-
## CLI
|
|
45
|
-
|
|
46
|
-
The same generator is available from the local CLI:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
node ./dist/cli.js demos/nornenv-region-refactor/.nornenv --refactor-region-pattern
|
|
50
|
-
node ./dist/cli.js demos/nornenv-region-refactor/.nornenv --refactor-region-pattern --write
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## When the refactor fires
|
|
54
|
-
|
|
55
|
-
The CodeLens appears only when **all** of these are true:
|
|
56
|
-
- At least 2 stages × 2 regions
|
|
57
|
-
- At least 3 populated cells
|
|
58
|
-
- All matrix envs are flat (no existing `extends` clause)
|
|
59
|
-
|
|
60
|
-
It does not touch envs that already use `extends`, envs with names that don't match the `STAGE_REGION` shape, or `connectionString` declarations (those are left in their leaves — refactor manually).
|
|
61
|
-
|
|
62
|
-
## Cmd+Z
|
|
63
|
-
|
|
64
|
-
Everything goes through a single `WorkspaceEdit`, so a single Cmd+Z undoes the whole refactor.
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# `.nornenv` Feature Showcase
|
|
2
|
-
|
|
3
|
-
A single `.nornenv` that touches every feature we've shipped — templates, multi-parent `extends`, diamond inheritance, secrets, connection-string templates, imports, plus every diagnostic. Open [.nornenv](./.nornenv) in VS Code and the editor experience does most of the talking.
|
|
4
|
-
|
|
5
|
-
## What to try, in order
|
|
6
|
-
|
|
7
|
-
1. **Open the file.** Non-active sections auto-collapse to one line; the smart header chips show `extends X, Y · N vars · K inherited · M overrides` plus any warning pills.
|
|
8
|
-
2. **Click `▶ Activate` on `[env:prod_eu]`.** It's the most feature-dense env. Watch the file change:
|
|
9
|
-
- The `ACTIVE` pill appears on its header
|
|
10
|
-
- A brand-gradient marker shows up in the gutter on its line
|
|
11
|
-
- A teal tick appears on the right-side overview ruler
|
|
12
|
-
- Every `{{...}}` reference in the file gets an inlay hint with the now-resolved value
|
|
13
|
-
- Secrets render as `(secret)` rather than the raw value
|
|
14
|
-
3. **Hover anything** — a variable name on the left side of a `=`, or a `{{ref}}`. The hover shows the resolution chain: where it's defined, whether it was inherited, and the current resolved value.
|
|
15
|
-
4. **Try `▶ Peek inherited`** on a leaf env like `[env:prod_eu]` — a quick-pick lists all 8+ inherited variables with their source templates and resolved values.
|
|
16
|
-
5. **Type `var ` inside `[env:prod_eu]`** — IntelliSense suggests every inherited variable name, so you can pick one to override without retyping.
|
|
17
|
-
6. **Start a new section header `[`** — completion offers both `[env:` and `[template:`. After typing `[env:foo `, completion offers `extends`, then suggests every available template as a parent.
|
|
18
|
-
7. **Scroll to the bottom** — the diagnostics-only block deliberately triggers every linter check:
|
|
19
|
-
- `[env:bad_unknown_parent extends prdo, us]` — `prdo` squiggles as `unknown-extends-parent` with a quick-fix suggesting `prod`
|
|
20
|
-
- `var apiKy = oops-typo` — squiggles as `unknown-override-target` with a suggestion
|
|
21
|
-
- `var dangling = {{kmsKey}}` — squiggles as `unresolved-cross-section-ref`
|
|
22
|
-
- `[env:bad_env_parent extends prod_us]` — `prod_us` squiggles as `extends-env-parent`
|
|
23
|
-
- `[template:loop_a]` / `[template:loop_b]` — both squiggle as `extends-cycle`
|
|
24
|
-
|
|
25
|
-
## What to try at the CLI
|
|
26
|
-
|
|
27
|
-
After `npm run compile`, run [showcase.norn](./showcase.norn) which prints every resolved value.
|
|
28
|
-
|
|
29
|
-
> **Heads-up about `{{…}}` inside `.nornenv` values:** the IDE's inlay hints show the **fully recursively resolved** form (e.g. `apiBase → "https://api.prod.example.com/v2"` under `prod_eu`). The `.norn` runtime substitution is single-level — when you print `{{apiBase}}` from a `.norn` file, you'll see the literal `https://api.{{stageHost}}.example.com/{{apiVersion}}` because `apiBase`'s *value* still contains template tokens. That's standard Norn behavior — switch to the IDE for the deep view, or use leaf vars (stageHost, region, dbHost, …) for clean CLI prints.
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# Most feature-dense env (self-override + diamond touchpoints).
|
|
33
|
-
node ./dist/cli.js demos/nornenv-showcase/showcase.norn -e prod_eu
|
|
34
|
-
|
|
35
|
-
# Right-most-wins demo — prod_strict (rightmost prod-chain template) wins on collisions.
|
|
36
|
-
node ./dist/cli.js demos/nornenv-showcase/showcase.norn -e prod_eu_strict
|
|
37
|
-
|
|
38
|
-
# Diamond inheritance — shared ancestor merged once.
|
|
39
|
-
node ./dist/cli.js demos/nornenv-showcase/showcase.norn -e diamond
|
|
40
|
-
|
|
41
|
-
# Templates are not selectable — this should error and list only env names.
|
|
42
|
-
node ./dist/cli.js demos/nornenv-showcase/showcase.norn -e prod
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## What each section demonstrates
|
|
46
|
-
|
|
47
|
-
| Section | Feature |
|
|
48
|
-
| --- | --- |
|
|
49
|
-
| `import "./shared/.nornenv"` | Multi-file composition; chained imports merge into common |
|
|
50
|
-
| top-of-file `var ...` | Common variables (shared by every env) |
|
|
51
|
-
| `var apiBase = https://api.{{stageHost}}…` | Common values may reference any name — resolves under the active env |
|
|
52
|
-
| `[template:dev/staging/prod]` | Stage building blocks (not selectable) |
|
|
53
|
-
| `[template:prod_strict extends prod]` | Template extending another template (chained) |
|
|
54
|
-
| `[template:us/eu]` | Region building blocks |
|
|
55
|
-
| `[template:db_main]` with `connectionString` + `secret connectionString` | Connection-string templates, plain and secret |
|
|
56
|
-
| `[env:dev_us extends dev, us, db_main]` | Composing 3 templates into one selectable env |
|
|
57
|
-
| `[env:prod_eu] ... var logLevel = error` | Self-override wins over every parent |
|
|
58
|
-
| `[env:prod_eu_strict extends prod_strict, eu, db_main]` | Right-most parent wins on collisions |
|
|
59
|
-
| `[env:diamond extends dev, prod_strict]` | Diamond: shared ancestor merged once |
|
|
60
|
-
| `[env:bad_unknown_parent ...]` | Unknown parent + typo override + dangling ref diagnostics |
|
|
61
|
-
| `[env:bad_env_parent extends prod_us]` | Env-as-parent diagnostic |
|
|
62
|
-
| `[template:loop_a/loop_b]` | Cycle diagnostic |
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"sql": {
|
|
4
|
-
"connections": {
|
|
5
|
-
"main": {
|
|
6
|
-
"adapter": "showcase-fake-adapter",
|
|
7
|
-
"profile": "main"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"adapters": {
|
|
11
|
-
"showcase-fake-adapter": {
|
|
12
|
-
"command": ["node", "-e", "process.stderr.write('showcase: SQL adapter not wired; this file is a visual demo only.\\n'); process.exit(1);"]
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Showcase .norn — exercises the inlay hints + hover from src/nornInlayHintsProvider
|
|
2
|
-
# and src/nornHoverProvider across all three scopes:
|
|
3
|
-
# 1. Sequence-local `var` declarations inside a `test sequence ... end sequence` block
|
|
4
|
-
# 2. File-level `var` declarations (above any sequence)
|
|
5
|
-
# 3. Active .nornenv environment (common + ancestor templates + self)
|
|
6
|
-
#
|
|
7
|
-
# Open this file with [env:prod_eu] active to watch every {{...}} reference
|
|
8
|
-
# resolve inline; hover any reference for the source + value resolution chain.
|
|
9
|
-
#
|
|
10
|
-
# CLI verification:
|
|
11
|
-
# node ./dist/cli.js demos/nornenv-showcase/showcase.norn -s ShowcaseResolvedValues -e prod_eu
|
|
12
|
-
# node ./dist/cli.js demos/nornenv-showcase/showcase.norn -s ShowcaseResolvedValues -e prod_eu_strict
|
|
13
|
-
# node ./dist/cli.js demos/nornenv-showcase/showcase.norn -s ShowcaseResolvedValues -e diamond
|
|
14
|
-
|
|
15
|
-
# ─── File-level vars (scope 2) ──────────────────────────────────────────────
|
|
16
|
-
# These reference env vars defined in showcase.nornenv ({{region}}, {{apiVersion}}).
|
|
17
|
-
# Inlay hints should resolve them recursively under the active env.
|
|
18
|
-
var siteTag = "showcase-{{region}}"
|
|
19
|
-
var docsHomepage = "{{apiBase}}/help"
|
|
20
|
-
|
|
21
|
-
# ─── Sequence 1: print every resolved env-level value ───────────────────────
|
|
22
|
-
test sequence ShowcaseResolvedValues
|
|
23
|
-
print "apiBase" | "{{apiBase}}"
|
|
24
|
-
print "dataBucket" | "{{dataBucket}}"
|
|
25
|
-
print "docsUrl" | "{{docsUrl}}"
|
|
26
|
-
print "stageHost" | "{{stageHost}}"
|
|
27
|
-
print "region" | "{{region}}"
|
|
28
|
-
print "dbHost" | "{{dbHost}}"
|
|
29
|
-
print "logLevel" | "{{logLevel}}"
|
|
30
|
-
print "retries" | "{{retries}}"
|
|
31
|
-
print "orgName" | "{{orgName}}"
|
|
32
|
-
print "main_connectionString" | "{{main_connectionString}}"
|
|
33
|
-
print "reports_connectionString" | "{{reports_connectionString}}"
|
|
34
|
-
print "apiKey" | "{{apiKey}}"
|
|
35
|
-
end sequence
|
|
36
|
-
|
|
37
|
-
# ─── Sequence 2: demonstrate all three scopes in one place ──────────────────
|
|
38
|
-
test sequence ShowcaseScopeLayers
|
|
39
|
-
# File-level var (scope 2) referenced from inside a sequence
|
|
40
|
-
print "siteTag" | "{{siteTag}}"
|
|
41
|
-
print "docsHomepage" | "{{docsHomepage}}"
|
|
42
|
-
|
|
43
|
-
# Sequence-local var (scope 1) — static value
|
|
44
|
-
var greeting = "Hello from {{stageHost}}"
|
|
45
|
-
print "greeting" | "{{greeting}}"
|
|
46
|
-
|
|
47
|
-
# Sequence-local var (scope 1) — composes env + region
|
|
48
|
-
var fullUserUrl = "{{apiBase}}/{{region}}/users"
|
|
49
|
-
print "fullUserUrl" | "{{fullUserUrl}}"
|
|
50
|
-
|
|
51
|
-
# Sequence-local var (scope 1) — overrides a file-level name within this sequence
|
|
52
|
-
var siteTag = "scoped-override-{{stageHost}}"
|
|
53
|
-
print "siteTag (local override)" | "{{siteTag}}"
|
|
54
|
-
|
|
55
|
-
# Explicit env-only reference — bypasses local/file scope, reads directly from env
|
|
56
|
-
print "env-only region" | "{{$env.region}}"
|
|
57
|
-
end sequence
|
|
58
|
-
|
|
59
|
-
# ─── Sequence 3: runtime vars are intentionally NOT shown by inlay hints ────
|
|
60
|
-
test sequence ShowcaseRuntimeRefs
|
|
61
|
-
# Sequence-local but RUNTIME (response capture) — no inlay hint expected on {{traceId}}.
|
|
62
|
-
# Hover on the reference will narrate "runtime expression" with source line.
|
|
63
|
-
GET https://httpbin.org/uuid
|
|
64
|
-
assert $1.status == 200
|
|
65
|
-
var traceId = $1.body.uuid
|
|
66
|
-
print "traceId" | "{{traceId}}"
|
|
67
|
-
|
|
68
|
-
# Response refs like {{$1.body.uuid}} are also runtime-only — no inlay hint.
|
|
69
|
-
print "uuid" | "{{$1.body.uuid}}"
|
|
70
|
-
end sequence
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Showcase .nornapi — every {{...}} on this file resolves under the active env via
|
|
2
|
-
# the inlay hints from src/nornapiInlayHintsProvider.ts. Hover any reference for
|
|
3
|
-
# the resolution chain.
|
|
4
|
-
#
|
|
5
|
-
# Try: activate [env:prod_eu] from showcase.nornenv and watch the URLs/headers
|
|
6
|
-
# below resolve to prod_eu's effective values.
|
|
7
|
-
|
|
8
|
-
headers Standard
|
|
9
|
-
Content-Type: application/json
|
|
10
|
-
Accept: application/json
|
|
11
|
-
end headers
|
|
12
|
-
|
|
13
|
-
headers AuthHeaders
|
|
14
|
-
Authorization: Bearer {{apiKey}}
|
|
15
|
-
X-Region: {{region}}
|
|
16
|
-
X-Stage: {{stageHost}}
|
|
17
|
-
X-Api-Version: {{apiVersion}}
|
|
18
|
-
end headers
|
|
19
|
-
|
|
20
|
-
endpoints
|
|
21
|
-
HealthCheck: GET {{apiBase}}/health
|
|
22
|
-
ListCustomers: GET {{apiBase}}/customers?region={{region}}
|
|
23
|
-
GetCustomer: GET {{apiBase}}/customers/{customerId}
|
|
24
|
-
CreateOrder: POST {{apiBase}}/orders
|
|
25
|
-
Failover: GET {{failoverHost}}/status
|
|
26
|
-
end endpoints
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Showcase .nornsql — the `connection main` line below is resolved by
|
|
2
|
-
# src/nornsqlInlayHintsProvider.ts to `main_connectionString` from the
|
|
3
|
-
# nearest .nornenv. Hover the identifier `main` to see where it's defined
|
|
4
|
-
# in [template:db_main] and what it resolves to under the active env.
|
|
5
|
-
#
|
|
6
|
-
# Try: activate [env:prod_eu] and watch the inlay after `main` show the
|
|
7
|
-
# resolved Server=tcp:db.eu.example.com,1433;... connection string.
|
|
8
|
-
|
|
9
|
-
connection main
|
|
10
|
-
|
|
11
|
-
query ListBuyers(region)
|
|
12
|
-
select Id, Company, Email
|
|
13
|
-
from Buyers
|
|
14
|
-
where Region = :region
|
|
15
|
-
end query
|
|
16
|
-
|
|
17
|
-
command RecordAuditEntry(actor, action)
|
|
18
|
-
insert into Audit (Actor, Action, At)
|
|
19
|
-
values (:actor, :action, current_timestamp)
|
|
20
|
-
end command
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
GET https://httpbin.org/uuid
|
|
3
|
-
|
|
4
|
-
GET https://httpbin.org/anything/catalog?region=uk&segment=enterprise
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
POST https://httpbin.org/post
|
|
11
|
-
Content-Type: application/json
|
|
12
|
-
Accept: application/json
|
|
13
|
-
X-Demo-Stage: exploratory-json
|
|
14
|
-
{
|
|
15
|
-
"customerId": "C-1001",
|
|
16
|
-
"workspace": "Northwind",
|
|
17
|
-
"plan": "Enterprise"
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
POST https://httpbin.org/post
|
|
22
|
-
Content-Type: application/json
|
|
23
|
-
Accept: application/json
|
|
24
|
-
X-Demo-Stage: exploratory-quote
|
|
25
|
-
X-Demo-Audience: exploratory-testers
|
|
26
|
-
{
|
|
27
|
-
"customerId": "C-1001",
|
|
28
|
-
"step": "quote-preview",
|
|
29
|
-
"seats": 250,
|
|
30
|
-
"currency": "GBP"
|
|
31
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
@demo
|
|
4
|
-
test sequence SimpleRequestChain
|
|
5
|
-
print "Simple requests" | "Bare URLs become a repeatable flow with assertions."
|
|
6
|
-
|
|
7
|
-
GET https://httpbin.org/uuid
|
|
8
|
-
assert $1.status == 200
|
|
9
|
-
var traceId = $1.body.uuid
|
|
10
|
-
|
|
11
|
-
GET https://httpbin.org/anything/catalog?region=uk&segment=enterprise&trace={{traceId}}
|
|
12
|
-
assert $2.status == 200
|
|
13
|
-
assert $2.body.args.region == "uk"
|
|
14
|
-
assert $2.body.args.segment == "enterprise"
|
|
15
|
-
assert $2.body.url contains "{{traceId}}"
|
|
16
|
-
|
|
17
|
-
print "Trace id" | "{{traceId}}"
|
|
18
|
-
end sequence
|
|
19
|
-
|
|
20
|
-
@demo
|
|
21
|
-
test sequence RichRequestChain
|
|
22
|
-
print "Headers and payloads" | "The same idea works when the requests carry real data."
|
|
23
|
-
|
|
24
|
-
var draft = POST https://httpbin.org/post
|
|
25
|
-
Content-Type: application/json
|
|
26
|
-
Accept: application/json
|
|
27
|
-
X-Demo-Stage: sequence-draft
|
|
28
|
-
{
|
|
29
|
-
"customerId": "C-1001",
|
|
30
|
-
"workspace": "Northwind",
|
|
31
|
-
"plan": "Enterprise"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
assert draft.status == 200
|
|
35
|
-
assert draft.body.json.customerId == "C-1001"
|
|
36
|
-
var customerId = draft.body.json.customerId
|
|
37
|
-
|
|
38
|
-
var quote = POST https://httpbin.org/post
|
|
39
|
-
Content-Type: application/json
|
|
40
|
-
Accept: application/json
|
|
41
|
-
X-Demo-Stage: sequence-quote
|
|
42
|
-
{
|
|
43
|
-
"customerId": "{{customerId}}",
|
|
44
|
-
"step": "quote-preview",
|
|
45
|
-
"seats": 250,
|
|
46
|
-
"currency": "GBP"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
assert quote.status == 200
|
|
50
|
-
assert quote.body.json.customerId == "C-1001"
|
|
51
|
-
assert quote.body.json.step == "quote-preview"
|
|
52
|
-
|
|
53
|
-
print "Chained value" | "customerId={{customerId}}"
|
|
54
|
-
end sequence
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import "./demo-api.nornapi"
|
|
2
|
-
|
|
3
|
-
@demo
|
|
4
|
-
test sequence CleanerExplorationFlow
|
|
5
|
-
print "Cleaner requests" | "Shared setup moved into .nornapi and .nornenv."
|
|
6
|
-
|
|
7
|
-
var trace = GET GetTraceId
|
|
8
|
-
assert trace.status == 200
|
|
9
|
-
|
|
10
|
-
var catalog = GET BrowseCustomer({{demoCustomerId}}) DemoHeaders
|
|
11
|
-
assert catalog.status == 200
|
|
12
|
-
assert catalog.body.args.region == "uk"
|
|
13
|
-
assert catalog.body.url contains "/customers/C-1001"
|
|
14
|
-
|
|
15
|
-
print "Why this helps" | "The request lines stay focused on intent, not plumbing."
|
|
16
|
-
end sequence
|
|
17
|
-
|
|
18
|
-
@demo
|
|
19
|
-
test sequence CleanerPayloadFlow
|
|
20
|
-
var draft = POST SaveDraft Json DemoHeaders
|
|
21
|
-
{
|
|
22
|
-
"customerId": "{{demoCustomerId}}",
|
|
23
|
-
"workspace": "{{workspaceName}}",
|
|
24
|
-
"owner": "{{ownerName}}"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
assert draft.status == 200
|
|
28
|
-
assert draft.body.json.workspace == "Northwind-Workspace"
|
|
29
|
-
|
|
30
|
-
var quote = POST QuotePreview Json DemoHeaders
|
|
31
|
-
{
|
|
32
|
-
"customerId": "{{demoCustomerId}}",
|
|
33
|
-
"traceId": "{{tracePrefix}}-001",
|
|
34
|
-
"step": "quote-preview",
|
|
35
|
-
"seats": 250
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
assert quote.status == 200
|
|
39
|
-
assert quote.body.json.step == "quote-preview"
|
|
40
|
-
|
|
41
|
-
print "Cleaner file" | "Base URL, reusable headers, and shared names are all sidecars now."
|
|
42
|
-
end sequence
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import "./demo-api.nornapi"
|
|
2
|
-
import "./db//testDb.nornsql"
|
|
3
|
-
|
|
4
|
-
@demo
|
|
5
|
-
test sequence ApiPlusSqlDemo
|
|
6
|
-
print "API plus SQL" | "Show the service call and the data-side check in one workflow."
|
|
7
|
-
|
|
8
|
-
var quote = POST QuotePreview Json DemoHeaders
|
|
9
|
-
{
|
|
10
|
-
"customerId": "{{demoCustomerId}}",
|
|
11
|
-
"step": "quote-preview",
|
|
12
|
-
"seats": 250,
|
|
13
|
-
"currency": "GBP"
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
assert quote.status == 200
|
|
17
|
-
assert quote.body.json.customerId == "C-1001"
|
|
18
|
-
|
|
19
|
-
var buyers = run sql ListBuyerAccounts("Enterprise")
|
|
20
|
-
assert buyers[0].Segment == "Enterprise"
|
|
21
|
-
assert buyers[0].Company == "Northwind Retail"
|
|
22
|
-
|
|
23
|
-
var audit = run sql RecordDemoRun("northwind@example.com", "buyer-showcase")
|
|
24
|
-
assert audit.affectedRows == 1
|
|
25
|
-
|
|
26
|
-
print "SQL sidecar" | "HTTP stays in .norn, SQL stays in .nornsql, config stays in sidecars."
|
|
27
|
-
end sequence
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
connection buyerDemoDb
|
|
2
|
-
|
|
3
|
-
query ListBuyerAccounts(segment)
|
|
4
|
-
select Id, Company, Email, Segment
|
|
5
|
-
from Buyers
|
|
6
|
-
where Segment = :segment
|
|
7
|
-
end query
|
|
8
|
-
|
|
9
|
-
command RecordDemoRun(email, scenario)
|
|
10
|
-
insert into DemoAudit (Email, Scenario)
|
|
11
|
-
values (:email, :scenario)
|
|
12
|
-
end command
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
headers Json
|
|
2
|
-
Content-Type: application/json
|
|
3
|
-
Accept: application/json
|
|
4
|
-
end headers
|
|
5
|
-
|
|
6
|
-
headers DemoHeaders
|
|
7
|
-
X-Demo-Stage: {{demoStage}}
|
|
8
|
-
X-Demo-Audience: {{audience}}
|
|
9
|
-
X-Demo-Owner: {{ownerName}}
|
|
10
|
-
end headers
|
|
11
|
-
|
|
12
|
-
endpoints
|
|
13
|
-
GetTraceId: GET {{baseUrl}}/uuid
|
|
14
|
-
BrowseCustomer: GET {{baseUrl}}/anything/customers/{customerId}?region={{region}}
|
|
15
|
-
SaveDraft: POST {{baseUrl}}/post
|
|
16
|
-
QuotePreview: POST {{baseUrl}}/post
|
|
17
|
-
end endpoints
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"sql": {
|
|
4
|
-
"connections": {
|
|
5
|
-
"buyerDemoDb": {
|
|
6
|
-
"adapter": "buyer-demo-sql-adapter",
|
|
7
|
-
"profile": "buyerDemoDb"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"adapters": {
|
|
11
|
-
"buyer-demo-sql-adapter": {
|
|
12
|
-
"command": ["node", "./scripts/fake-sql-adapter.js"]
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
package/playground/ai.norn
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import "./ai_orchastration.nornagent"
|
|
2
|
-
|
|
3
|
-
sequence SimpleNexusRun
|
|
4
|
-
|
|
5
|
-
var nexusReply = run Nexus "Open any website for me need to see if the mcp connection is working"
|
|
6
|
-
|
|
7
|
-
print "Result" | nexusReply
|
|
8
|
-
|
|
9
|
-
end sequence
|
|
10
|
-
|
|
11
|
-
sequence Test
|
|
12
|
-
run mcp list Time
|
|
13
|
-
run TimeTeller "what is the time"
|
|
14
|
-
|
|
15
|
-
end sequence
|