@cynodia/axiom-cli 0.16.0-alpha.2
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/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +642 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AskTech AS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Axiom CLI
|
|
2
|
+
|
|
3
|
+
Part of [Axiom](https://github.com/cynodia/axiom), an AI-native semantic web application
|
|
4
|
+
framework.
|
|
5
|
+
|
|
6
|
+
**Status: experimental / alpha.** The API may change between alpha releases.
|
|
7
|
+
|
|
8
|
+
**AI agents:** read `docs/AGENT_REFERENCE.md` and `docs/AGENT_API.md` inside the installed
|
|
9
|
+
`@cynodia/axiom` package before authoring or inspecting an application. This tool is a thin
|
|
10
|
+
renderer over that package's `AgentAPI` — it implements no semantic analysis of its own
|
|
11
|
+
(spec16pt2 §53).
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install --global @cynodia/axiom-cli
|
|
17
|
+
# or, without installing:
|
|
18
|
+
npx @cynodia/axiom-cli --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This installs the `axiom` executable. Every command takes a **compiled** (built) JavaScript
|
|
22
|
+
module — `<modelFile>` — that exports an `ApplicationGraph`, or a function that builds one
|
|
23
|
+
(`export function createGraph() { … }`, `export default …`); pass `--export=<name>` when a
|
|
24
|
+
module exports more than one candidate.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
axiom build <modelFile> [--export=name]
|
|
30
|
+
axiom inspect <modelFile> [--export=name]
|
|
31
|
+
axiom validate <modelFile> [--export=name] [--json]
|
|
32
|
+
axiom serve <modelFile> [--export=name] [--port=3000] [--store=state.db]
|
|
33
|
+
|
|
34
|
+
axiom schema status <modelFile> [--export=name]
|
|
35
|
+
axiom schema diff <modelFile> --against=<prevFile> [--export=name] [--against-export=name]
|
|
36
|
+
axiom migrate plan <modelFile> [--export=name] [--from=<version>]
|
|
37
|
+
axiom migrate <modelFile> [--export=name] [--from=<version>] [--approve=op1,op2] [--sqlite=<path>]
|
|
38
|
+
axiom migrate status <modelFile> --sqlite=<path>
|
|
39
|
+
|
|
40
|
+
axiom explain <action|query|workflow|state> <id> <modelFile> [--json]
|
|
41
|
+
axiom analyze <modelFile> [--json]
|
|
42
|
+
axiom diff <modelFile> --against=<prevFile> [--export=name] [--against-export=name] [--json]
|
|
43
|
+
|
|
44
|
+
axiom --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`explain`, `analyze` and `diff` are the canonical tooling surface spec16 defines
|
|
48
|
+
(`docs/AGENT_API.md`'s "Semantic inventory, dependencies and explanation" section):
|
|
49
|
+
|
|
50
|
+
- **`explain`** renders `AgentAPI.explainAction` / `.explainQuery` / `.explainWorkflow` /
|
|
51
|
+
`.explainState` for one node — reads/writes, authorization, invokers, live capability,
|
|
52
|
+
and (for actions) whether static analysis is complete or a `NativeOperation` boundary
|
|
53
|
+
prevents it.
|
|
54
|
+
- **`analyze`** renders a structural graph summary, required runtime capabilities, the
|
|
55
|
+
`NativeOperation` inventory and the authorization coverage audit
|
|
56
|
+
(`AgentAPI.explainGraph` / `.analyzeCapabilities` / `.summarizeNativeOperations` /
|
|
57
|
+
`.analyzeAuthorization`).
|
|
58
|
+
- **`diff`** renders `AgentAPI.semanticDiff` between two graphs: every categorized change
|
|
59
|
+
(`semantic` / `authorization` / `schema` / `provider` / `workflow` / `query` /
|
|
60
|
+
`presentation` / `metadata`) and the compatibility impact — does it move
|
|
61
|
+
`semanticFingerprint`, `schemaFingerprint`, or the required Server IR contract. An
|
|
62
|
+
authorization-relevant change (attaching, detaching or editing a policy — including a
|
|
63
|
+
`QueryDef`'s row-level `readPolicyId`) is always tagged `authorization`, in addition to
|
|
64
|
+
its own category, never in place of it.
|
|
65
|
+
|
|
66
|
+
## Machine-readable output
|
|
67
|
+
|
|
68
|
+
Pass `--json` on `explain`, `analyze` or `diff` for structured output — parseable,
|
|
69
|
+
deterministic, and semantically identical to the corresponding `AgentAPI` result (no human
|
|
70
|
+
terminal decoration reaches `--json` mode). Everything else prints a concise human-readable
|
|
71
|
+
rendering.
|
|
72
|
+
|
|
73
|
+
## Exit codes
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
0 the requested operation completed (including a successful `explain`/`analyze`/`diff`,
|
|
77
|
+
and `validate` on a graph that is valid)
|
|
78
|
+
nonzero invalid input, an invalid graph (`validate`), or a tooling failure
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`--help` (or no arguments) always prints usage and exits `0`.
|
|
82
|
+
|
|
83
|
+
## Side effects
|
|
84
|
+
|
|
85
|
+
`explain`, `analyze`, `diff`, `inspect` and `validate` are read-only static analysis: they
|
|
86
|
+
load the graph and never invoke an action, start a workflow, create an effect or write
|
|
87
|
+
persistence. `build` writes a compiled HTML file to `./dist`; `migrate` (without `--sqlite`)
|
|
88
|
+
only plans and prints — it executes only when given `--sqlite=<path>`; `serve` starts a real
|
|
89
|
+
HTTP server and, if the graph has server authority, a real authoritative runtime.
|
|
90
|
+
|
|
91
|
+
## Scope
|
|
92
|
+
|
|
93
|
+
This is a development and inspection tool, not a production hosting mechanism. Publishing
|
|
94
|
+
it does not imply exposing `AgentAPI` over an unauthenticated network endpoint — `serve`'s
|
|
95
|
+
own semantic endpoint is the only network surface any command opens, and only when asked to
|
|
96
|
+
`serve` in the first place.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { compileToHtml, compileToIR, compileToServerIR, hasServerAuthority } from '@cynodia/axiom-compiler';
|
|
7
|
+
import { createAxiomServer, createMemoryPersistence, createServerHost, createSqliteMigrationStore, createSqlitePersistence, createSqliteRowStore, dispatch, executeMigration, explainMigration, getMigrationStatus, isSqliteAvailable, migrationAuthority, planMigration, } from '@cynodia/axiom-server';
|
|
8
|
+
import { ApplicationGraph, diffSchema, formatLocation, semanticContextFromGraph, semanticDiff, validateGraph } from '@cynodia/axiom-core';
|
|
9
|
+
import { AgentAPI, explainSchemaDiff, inspectSchema, migrationImpact } from '@cynodia/axiom-agent-api';
|
|
10
|
+
const GRAPH_EXPORT_CANDIDATES = ['default', 'createGraph', 'createApplicationGraph'];
|
|
11
|
+
/** `<group> <sub>` command forms. `migrate` alone (no sub) is also valid. */
|
|
12
|
+
const SUBCOMMANDS = {
|
|
13
|
+
schema: new Set(['status', 'diff']),
|
|
14
|
+
migrate: new Set(['plan', 'status']),
|
|
15
|
+
};
|
|
16
|
+
function parseArguments(argv) {
|
|
17
|
+
const positional = [];
|
|
18
|
+
let exportName;
|
|
19
|
+
let port = 3000;
|
|
20
|
+
let store;
|
|
21
|
+
let from;
|
|
22
|
+
let approve;
|
|
23
|
+
let sqlite;
|
|
24
|
+
let against;
|
|
25
|
+
let againstExport;
|
|
26
|
+
let json = false;
|
|
27
|
+
for (const argument of argv) {
|
|
28
|
+
if (argument.startsWith('--export=')) {
|
|
29
|
+
exportName = argument.slice('--export='.length);
|
|
30
|
+
}
|
|
31
|
+
else if (argument.startsWith('--port=')) {
|
|
32
|
+
port = Number(argument.slice('--port='.length)) || port;
|
|
33
|
+
}
|
|
34
|
+
else if (argument.startsWith('--store=')) {
|
|
35
|
+
store = argument.slice('--store='.length);
|
|
36
|
+
}
|
|
37
|
+
else if (argument.startsWith('--from=')) {
|
|
38
|
+
from = Number(argument.slice('--from='.length));
|
|
39
|
+
}
|
|
40
|
+
else if (argument.startsWith('--approve=')) {
|
|
41
|
+
approve = argument.slice('--approve='.length).split(',').filter(Boolean);
|
|
42
|
+
}
|
|
43
|
+
else if (argument.startsWith('--sqlite=')) {
|
|
44
|
+
sqlite = argument.slice('--sqlite='.length);
|
|
45
|
+
}
|
|
46
|
+
else if (argument.startsWith('--against=')) {
|
|
47
|
+
against = argument.slice('--against='.length);
|
|
48
|
+
}
|
|
49
|
+
else if (argument.startsWith('--against-export=')) {
|
|
50
|
+
againstExport = argument.slice('--against-export='.length);
|
|
51
|
+
}
|
|
52
|
+
else if (argument === '--json') {
|
|
53
|
+
json = true;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
positional.push(argument);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let [command, modelFile] = positional;
|
|
60
|
+
let kind;
|
|
61
|
+
let targetId;
|
|
62
|
+
if (command === 'explain') {
|
|
63
|
+
// `axiom explain <kind> <id> <modelFile>` — three positionals after the command itself.
|
|
64
|
+
kind = positional[1];
|
|
65
|
+
targetId = positional[2];
|
|
66
|
+
modelFile = positional[3];
|
|
67
|
+
}
|
|
68
|
+
else if (command && SUBCOMMANDS[command]?.has(positional[1] ?? '')) {
|
|
69
|
+
command = `${command} ${positional[1]}`;
|
|
70
|
+
modelFile = positional[2];
|
|
71
|
+
}
|
|
72
|
+
if (!command || !modelFile) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
command,
|
|
77
|
+
modelFile,
|
|
78
|
+
exportName,
|
|
79
|
+
port,
|
|
80
|
+
...(store ? { store } : {}),
|
|
81
|
+
...(from !== undefined ? { from } : {}),
|
|
82
|
+
...(approve ? { approve } : {}),
|
|
83
|
+
...(sqlite ? { sqlite } : {}),
|
|
84
|
+
...(against ? { against } : {}),
|
|
85
|
+
...(againstExport ? { againstExport } : {}),
|
|
86
|
+
...(kind ? { kind } : {}),
|
|
87
|
+
...(targetId ? { targetId } : {}),
|
|
88
|
+
...(json ? { json } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function toGraph(candidate) {
|
|
92
|
+
if (candidate instanceof ApplicationGraph) {
|
|
93
|
+
return candidate;
|
|
94
|
+
}
|
|
95
|
+
if (typeof candidate === 'string') {
|
|
96
|
+
return ApplicationGraph.deserialize(candidate);
|
|
97
|
+
}
|
|
98
|
+
if (candidate && typeof candidate === 'object' && 'nodes' in candidate && 'edges' in candidate) {
|
|
99
|
+
return ApplicationGraph.deserialize(candidate);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Loads an application graph from a module. The module may export the graph directly or
|
|
105
|
+
* a function that builds it; nothing about the application itself is assumed.
|
|
106
|
+
*/
|
|
107
|
+
async function loadGraph(options) {
|
|
108
|
+
return loadGraphModule(options.modelFile, options.exportName);
|
|
109
|
+
}
|
|
110
|
+
async function loadGraphModule(modelFile, exportName) {
|
|
111
|
+
const options = { modelFile, exportName };
|
|
112
|
+
const resolved = path.resolve(process.cwd(), options.modelFile);
|
|
113
|
+
const module = (await import(pathToFileURL(resolved).href));
|
|
114
|
+
const names = options.exportName
|
|
115
|
+
? [options.exportName]
|
|
116
|
+
: GRAPH_EXPORT_CANDIDATES.filter((name) => name in module);
|
|
117
|
+
for (const name of names) {
|
|
118
|
+
const exported = module[name];
|
|
119
|
+
const value = typeof exported === 'function' ? exported() : exported;
|
|
120
|
+
const graph = toGraph(value);
|
|
121
|
+
if (graph) {
|
|
122
|
+
return graph;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!options.exportName) {
|
|
126
|
+
const builders = Object.entries(module).filter(([, value]) => typeof value === 'function');
|
|
127
|
+
if (builders.length === 1) {
|
|
128
|
+
const graph = toGraph(builders[0][1]());
|
|
129
|
+
if (graph) {
|
|
130
|
+
return graph;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (builders.length > 1) {
|
|
134
|
+
throw new Error(`${options.modelFile} exports several candidates. Choose one with --export=<name>: ${builders
|
|
135
|
+
.map(([name]) => name)
|
|
136
|
+
.join(', ')}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Could not load an application graph from ${options.modelFile}`);
|
|
140
|
+
}
|
|
141
|
+
const SECTIONS = [
|
|
142
|
+
['Entities', 'entity'],
|
|
143
|
+
['State', 'state'],
|
|
144
|
+
['Actions', 'action'],
|
|
145
|
+
['Constraints', 'constraint'],
|
|
146
|
+
['Routes', 'route'],
|
|
147
|
+
['Views', 'view'],
|
|
148
|
+
];
|
|
149
|
+
function describe(node) {
|
|
150
|
+
const label = node.name ? `${node.name} (${node.id})` : node.id;
|
|
151
|
+
if (node.kind === 'entity') {
|
|
152
|
+
const fields = node.fields.map((field) => field.name ?? field.id).join(', ');
|
|
153
|
+
return `${label}${fields ? ` — fields: ${fields}` : ''}`;
|
|
154
|
+
}
|
|
155
|
+
if (node.kind === 'route') {
|
|
156
|
+
return `${label} — ${node.path}`;
|
|
157
|
+
}
|
|
158
|
+
return label;
|
|
159
|
+
}
|
|
160
|
+
/** Locations are stored by id and resolved to names only for human inspection. */
|
|
161
|
+
function describeOperation(operation, semantics) {
|
|
162
|
+
switch (operation.kind) {
|
|
163
|
+
case 'set':
|
|
164
|
+
return `set ${formatLocation(operation.target, semantics)}`;
|
|
165
|
+
case 'insert':
|
|
166
|
+
return `insert into ${formatLocation(operation.target, semantics)}`;
|
|
167
|
+
case 'remove':
|
|
168
|
+
return `remove ${formatLocation(operation.target, semantics)}`;
|
|
169
|
+
case 'invoke':
|
|
170
|
+
return `invoke ${semantics.getName?.(operation.actionId) ?? operation.actionId}`;
|
|
171
|
+
case 'navigate':
|
|
172
|
+
return `navigate ${operation.path ?? semantics.getName?.(operation.routeId) ?? operation.routeId}`;
|
|
173
|
+
case 'native':
|
|
174
|
+
return `native ${operation.implementationId}`;
|
|
175
|
+
default:
|
|
176
|
+
return 'unknown operation';
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function inspect(graph) {
|
|
180
|
+
const semantics = semanticContextFromGraph(graph);
|
|
181
|
+
const lines = [`${graph.name} (${graph.id}) v${graph.version}`, ''];
|
|
182
|
+
const fieldNames = (edge) => {
|
|
183
|
+
const fieldIds = edge.metadata?.fieldIds;
|
|
184
|
+
if (!Array.isArray(fieldIds) || fieldIds.length === 0) {
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
return ` (${fieldIds.map((id) => graph.getField(id)?.field.name ?? String(id)).join(', ')})`;
|
|
188
|
+
};
|
|
189
|
+
for (const [title, kind] of SECTIONS) {
|
|
190
|
+
const nodes = graph.getNodesByKind(kind);
|
|
191
|
+
lines.push(title);
|
|
192
|
+
if (nodes.length === 0) {
|
|
193
|
+
lines.push('- none');
|
|
194
|
+
}
|
|
195
|
+
for (const node of nodes) {
|
|
196
|
+
const edges = graph
|
|
197
|
+
.getOutgoingEdges(node.id)
|
|
198
|
+
.map((edge) => `${edge.kind} → ${graph.getNode(edge.to)?.name ?? edge.to}${fieldNames(edge)}`)
|
|
199
|
+
.join(', ');
|
|
200
|
+
lines.push(`- ${describe(node)}${edges ? ` [${edges}]` : ''}`);
|
|
201
|
+
if (node.kind === 'action') {
|
|
202
|
+
for (const operation of node.operations ?? []) {
|
|
203
|
+
lines.push(` ${describeOperation(operation, semantics)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
lines.push('');
|
|
208
|
+
}
|
|
209
|
+
const uiCount = graph.listNodes().filter((node) => !SECTIONS.some(([, kind]) => kind === node.kind)).length;
|
|
210
|
+
lines.push(`UI nodes: ${uiCount}`, `Edges: ${graph.semanticEdges().length}`);
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
}
|
|
213
|
+
function formatValidation(result) {
|
|
214
|
+
const lines = [];
|
|
215
|
+
for (const problem of result.errors) {
|
|
216
|
+
lines.push(`error [${problem.code}] ${problem.message}`);
|
|
217
|
+
}
|
|
218
|
+
for (const problem of result.warnings) {
|
|
219
|
+
lines.push(`warning [${problem.code}] ${problem.message}`);
|
|
220
|
+
}
|
|
221
|
+
lines.push(result.valid
|
|
222
|
+
? `Graph is valid (${result.warnings.length} warning${result.warnings.length === 1 ? '' : 's'}).`
|
|
223
|
+
: `Graph is invalid: ${result.errors.length} error${result.errors.length === 1 ? '' : 's'}.`);
|
|
224
|
+
return lines.join('\n');
|
|
225
|
+
}
|
|
226
|
+
async function build(options) {
|
|
227
|
+
const graph = await loadGraph(options);
|
|
228
|
+
const html = compileToHtml(graph);
|
|
229
|
+
const outputDir = path.resolve(process.cwd(), 'dist');
|
|
230
|
+
const outputFile = path.join(outputDir, `${graph.id}.html`);
|
|
231
|
+
await mkdir(outputDir, { recursive: true });
|
|
232
|
+
await writeFile(outputFile, html, 'utf8');
|
|
233
|
+
console.log(`Built ${outputFile}`);
|
|
234
|
+
}
|
|
235
|
+
/** The authoritative half, when the graph has one. No application code is involved. */
|
|
236
|
+
async function startAuthority(graph, options) {
|
|
237
|
+
if (!hasServerAuthority(graph)) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
let persistence;
|
|
241
|
+
if (options.store && (await isSqliteAvailable())) {
|
|
242
|
+
persistence = await createSqlitePersistence({ location: options.store });
|
|
243
|
+
console.log(`Authoritative state persists to ${options.store}`);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
if (options.store) {
|
|
247
|
+
console.warn('node:sqlite is unavailable; authoritative state is held in memory only');
|
|
248
|
+
}
|
|
249
|
+
persistence = createMemoryPersistence();
|
|
250
|
+
}
|
|
251
|
+
const server = createAxiomServer({
|
|
252
|
+
ir: compileToServerIR(graph),
|
|
253
|
+
persistence,
|
|
254
|
+
host: createServerHost({
|
|
255
|
+
// Authentication belongs to a host. This one reads a bearer credential and treats it
|
|
256
|
+
// as the caller's identity, which is enough to demonstrate the boundary and no more.
|
|
257
|
+
authenticate: (credential) => credential ? { [PRINCIPAL_IDENTITY]: credential } : null,
|
|
258
|
+
report: (event) => {
|
|
259
|
+
if (event.kind !== 'snapshot') {
|
|
260
|
+
console.log(`[axiom] ${event.kind} ${event.actionId ?? ''} ${event.ok === undefined ? '' : event.ok ? 'ok' : 'refused'}`.trim());
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
await server.start();
|
|
266
|
+
return server;
|
|
267
|
+
}
|
|
268
|
+
/** The identity field of the graph's principal entity, resolved at startup. */
|
|
269
|
+
let PRINCIPAL_IDENTITY = 'id';
|
|
270
|
+
async function serve(options) {
|
|
271
|
+
const graph = await loadGraph(options);
|
|
272
|
+
const ir = compileToIR(graph);
|
|
273
|
+
const html = compileToHtml(graph);
|
|
274
|
+
const principalEntity = graph.principalEntityId
|
|
275
|
+
? graph.getNode(graph.principalEntityId)
|
|
276
|
+
: undefined;
|
|
277
|
+
if (principalEntity?.kind === 'entity' && principalEntity.identityFieldId) {
|
|
278
|
+
PRINCIPAL_IDENTITY = String(principalEntity.identityFieldId);
|
|
279
|
+
}
|
|
280
|
+
const authority = await startAuthority(graph, options);
|
|
281
|
+
const matches = (pathname) => ir.routes.some((route) => {
|
|
282
|
+
const parts = pathname.split('?')[0].split('/').filter(Boolean);
|
|
283
|
+
return (route.segments.length === parts.length &&
|
|
284
|
+
route.segments.every((segment, index) => segment.kind === 'parameter' || segment.value === parts[index]));
|
|
285
|
+
});
|
|
286
|
+
const server = createServer((request, response) => {
|
|
287
|
+
void (async () => {
|
|
288
|
+
// One semantic endpoint, the same for every application. No route is declared here
|
|
289
|
+
// and none is generated: the client asks for actions, not for URLs.
|
|
290
|
+
if (authority && request.method === 'POST' && (request.url ?? '').split('?')[0] === SEMANTIC_ENDPOINT) {
|
|
291
|
+
const chunks = [];
|
|
292
|
+
for await (const chunk of request) {
|
|
293
|
+
chunks.push(chunk);
|
|
294
|
+
}
|
|
295
|
+
let body = null;
|
|
296
|
+
try {
|
|
297
|
+
body = chunks.length > 0 ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : null;
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
response.writeHead(400, { 'content-type': 'application/json' });
|
|
301
|
+
response.end('{"kind":"error","diagnostics":[]}');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const answer = await dispatch(authority, body);
|
|
305
|
+
response.writeHead(200, { 'content-type': 'application/json' });
|
|
306
|
+
response.end(JSON.stringify(answer));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (request.url && matches(request.url)) {
|
|
310
|
+
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
311
|
+
response.end(html);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
315
|
+
response.end('No route matches this path');
|
|
316
|
+
})();
|
|
317
|
+
});
|
|
318
|
+
server.listen(options.port, '127.0.0.1', () => {
|
|
319
|
+
console.log(`${graph.name} available at http://127.0.0.1:${options.port}`);
|
|
320
|
+
if (authority) {
|
|
321
|
+
console.log(`Authoritative runtime at http://127.0.0.1:${options.port}${SEMANTIC_ENDPOINT} — ` +
|
|
322
|
+
`${Object.keys(compileToServerIR(graph).actions).length} server actions`);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/** The one endpoint every Axiom authority answers on. */
|
|
327
|
+
const SEMANTIC_ENDPOINT = '/axiom';
|
|
328
|
+
// --- Schema evolution (spec11 §89-91) --------------------------------------
|
|
329
|
+
// Thin wrappers over the already-tested library functions. The CLI is a consumer,
|
|
330
|
+
// never the definition of behaviour.
|
|
331
|
+
function schemaStatus(graph) {
|
|
332
|
+
const s = inspectSchema(graph);
|
|
333
|
+
const lines = [
|
|
334
|
+
`semantic schema version : ${s.schemaVersion}`,
|
|
335
|
+
`schema fingerprint : ${s.schemaFingerprint}`,
|
|
336
|
+
`migration chain : ${s.chainComplete ? 'complete (1 → ' + s.schemaVersion + ')' : 'INCOMPLETE'}`,
|
|
337
|
+
`entities : ${s.entities.length}`,
|
|
338
|
+
`persisted states : ${s.persistedStates.length}`,
|
|
339
|
+
`relationships : ${s.relationships.length}`,
|
|
340
|
+
`read policies : ${s.readPolicies.length}`,
|
|
341
|
+
];
|
|
342
|
+
if (s.migrations.length > 0) {
|
|
343
|
+
lines.push('migrations:');
|
|
344
|
+
for (const migration of s.migrations) {
|
|
345
|
+
lines.push(` ${migration.fromSchema} → ${migration.toSchema} ${migration.id} (${migration.operationCount} ops, ${migration.destructiveOperationCount} destructive)`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return lines.join('\n');
|
|
349
|
+
}
|
|
350
|
+
async function schemaDiff(options) {
|
|
351
|
+
if (!options.against) {
|
|
352
|
+
throw new Error('schema diff needs a --against=<file> naming the previous schema');
|
|
353
|
+
}
|
|
354
|
+
const previous = await loadGraphModule(options.against, options.againstExport);
|
|
355
|
+
const next = await loadGraph(options);
|
|
356
|
+
const diff = diffSchema(previous, next);
|
|
357
|
+
const impact = migrationImpact(previous, next);
|
|
358
|
+
const parts = [
|
|
359
|
+
explainSchemaDiff(diff),
|
|
360
|
+
'',
|
|
361
|
+
`verdict : ${impact.verdict}`,
|
|
362
|
+
`data loss possible : ${impact.dataLossPossible}`,
|
|
363
|
+
`migration covers it: ${impact.covered}${impact.covered ? '' : ' — uncovered: ' + impact.uncovered.map((e) => e.fieldId ?? e.entityId).join(', ')}`,
|
|
364
|
+
`affected queries : ${impact.affectedQueries.length}`,
|
|
365
|
+
`affected actions : ${impact.affectedActions.length}`,
|
|
366
|
+
`affected constraints: ${impact.affectedConstraints.length}`,
|
|
367
|
+
`affected UI nodes : ${impact.affectedUiNodes.length}`,
|
|
368
|
+
];
|
|
369
|
+
if (impact.authorizationChanges.length > 0) {
|
|
370
|
+
parts.push(`authorization changes: ${impact.authorizationChanges.join(', ')}`);
|
|
371
|
+
}
|
|
372
|
+
return parts.join('\n');
|
|
373
|
+
}
|
|
374
|
+
async function migratePlan(options) {
|
|
375
|
+
const graph = await loadGraph(options);
|
|
376
|
+
const ir = compileToServerIR(graph, { validate: false });
|
|
377
|
+
const from = options.from ?? 1;
|
|
378
|
+
const result = planMigration(ir, { fromVersion: from });
|
|
379
|
+
if (!result.ok) {
|
|
380
|
+
process.exitCode = 1;
|
|
381
|
+
return result.diagnostics.map((d) => `${d.code}: ${d.message}`).join('\n');
|
|
382
|
+
}
|
|
383
|
+
return explainMigration(result.plan);
|
|
384
|
+
}
|
|
385
|
+
async function migrateStatus(options) {
|
|
386
|
+
if (!options.sqlite) {
|
|
387
|
+
throw new Error('migrate status needs --sqlite=<path> to read the provider metadata');
|
|
388
|
+
}
|
|
389
|
+
if (!(await isSqliteAvailable())) {
|
|
390
|
+
throw new Error('this Node build has no node:sqlite');
|
|
391
|
+
}
|
|
392
|
+
const metadata = await createSqliteMigrationStore({ location: options.sqlite });
|
|
393
|
+
const status = await getMigrationStatus(metadata);
|
|
394
|
+
return [
|
|
395
|
+
`schema version : ${status.schemaVersion ?? '(unstamped)'}`,
|
|
396
|
+
`fingerprint : ${status.schemaFingerprint ?? '(none)'}`,
|
|
397
|
+
`phase : ${status.phase}`,
|
|
398
|
+
`lock : ${status.lock ? status.lock.holder : 'free'}`,
|
|
399
|
+
`checkpoint : ${status.checkpoint ? `op ${status.checkpoint.operationIndex}, ${status.checkpoint.rowsProcessed} rows` : 'none'}`,
|
|
400
|
+
`history : ${status.history.map((h) => `${h.fromSchema}→${h.toSchema}`).join(', ') || 'none'}`,
|
|
401
|
+
].join('\n');
|
|
402
|
+
}
|
|
403
|
+
async function migrateRun(options) {
|
|
404
|
+
if (!options.sqlite) {
|
|
405
|
+
return `${await migratePlan(options)}\n\n(supply --sqlite=<path> to execute this migration against a SQLite database)`;
|
|
406
|
+
}
|
|
407
|
+
if (!(await isSqliteAvailable())) {
|
|
408
|
+
throw new Error('this Node build has no node:sqlite');
|
|
409
|
+
}
|
|
410
|
+
const graph = await loadGraph(options);
|
|
411
|
+
const ir = compileToServerIR(graph, { validate: false });
|
|
412
|
+
const rows = await createSqliteRowStore({ location: options.sqlite, ir });
|
|
413
|
+
const metadata = await createSqliteMigrationStore({
|
|
414
|
+
location: options.sqlite,
|
|
415
|
+
database: rows.database,
|
|
416
|
+
});
|
|
417
|
+
const result = await executeMigration({
|
|
418
|
+
ir,
|
|
419
|
+
metadata,
|
|
420
|
+
rows,
|
|
421
|
+
principal: migrationAuthority('axiom-cli'),
|
|
422
|
+
...(options.from !== undefined ? { fromVersion: options.from } : {}),
|
|
423
|
+
approveDestructive: options.approve ?? [],
|
|
424
|
+
});
|
|
425
|
+
if (!result.ok) {
|
|
426
|
+
process.exitCode = 1;
|
|
427
|
+
return `${result.code}: ${result.message}`;
|
|
428
|
+
}
|
|
429
|
+
return [
|
|
430
|
+
`migrated ${result.plan.fromVersion} → ${result.plan.toVersion}`,
|
|
431
|
+
`rows transformed : ${result.run.rowsTransformed}`,
|
|
432
|
+
`resumed : ${result.run.resumed}`,
|
|
433
|
+
`gate now : ${result.gate.status}`,
|
|
434
|
+
].join('\n');
|
|
435
|
+
}
|
|
436
|
+
// --- Explainability & AI authoring tooling (spec16 §106-112) ---------------
|
|
437
|
+
// Thin renderers over AgentAPI / core, exactly like the schema commands above:
|
|
438
|
+
// the CLI is a consumer of the canonical analysis, never a second place it lives.
|
|
439
|
+
function formatExplanation(kind, result) {
|
|
440
|
+
const lines = [];
|
|
441
|
+
switch (kind) {
|
|
442
|
+
case 'action': {
|
|
443
|
+
const reads = result.reads;
|
|
444
|
+
const writes = result.writes;
|
|
445
|
+
const authorization = result.authorization;
|
|
446
|
+
const invokedBy = result.invokedBy;
|
|
447
|
+
lines.push(`action ${result.actionId}${result.name ? ` (${result.name})` : ''}`);
|
|
448
|
+
lines.push(` reads : ${reads.stateIds.join(', ') || 'none'}`);
|
|
449
|
+
lines.push(` writes : ${writes.stateIds.join(', ') || 'none'}`);
|
|
450
|
+
lines.push(` authorization: ${authorization.kind}`);
|
|
451
|
+
lines.push(` invoked by: triggers [${invokedBy.triggers.join(', ')}], workflows [${invokedBy.workflowSteps.join(', ')}]`);
|
|
452
|
+
if (result.analysisComplete === false) {
|
|
453
|
+
lines.push(` INCOMPLETE — ${result.analysisGaps.join('; ')}`);
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case 'query': {
|
|
458
|
+
const authorization = result.authorization;
|
|
459
|
+
lines.push(`query ${result.queryId}`);
|
|
460
|
+
lines.push(` source : ${result.source}`);
|
|
461
|
+
lines.push(` authorization : ${authorization.kind}`);
|
|
462
|
+
lines.push(` live capability: ${result.liveCapability}`);
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
case 'workflow': {
|
|
466
|
+
const steps = result.steps;
|
|
467
|
+
lines.push(`workflow ${result.workflowId}`);
|
|
468
|
+
lines.push(` steps : ${steps.map((s) => `${s.id}:${s.type}`).join(', ')}`);
|
|
469
|
+
lines.push(` start policy: ${result.startPolicyId ?? 'public'}`);
|
|
470
|
+
lines.push(` privilege-review actions: ${result.privilegeReviewActions.join(', ') || 'none'}`);
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
case 'state': {
|
|
474
|
+
lines.push(`state ${result.stateId}`);
|
|
475
|
+
lines.push(` authority : ${result.authority} derived: ${result.derived} draft: ${result.draft}`);
|
|
476
|
+
lines.push(` writers : ${result.writers.join(', ') || 'none'}`);
|
|
477
|
+
lines.push(` readers : ${result.readers.join(', ') || 'none'}`);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
default:
|
|
481
|
+
lines.push(JSON.stringify(result, null, 2));
|
|
482
|
+
}
|
|
483
|
+
return lines.join('\n');
|
|
484
|
+
}
|
|
485
|
+
async function explainCommand(options) {
|
|
486
|
+
if (!options.kind || !options.targetId) {
|
|
487
|
+
throw new Error('usage: axiom explain <action|query|workflow|state> <id> <modelFile>');
|
|
488
|
+
}
|
|
489
|
+
const agent = new AgentAPI(await loadGraph(options));
|
|
490
|
+
let result;
|
|
491
|
+
switch (options.kind) {
|
|
492
|
+
case 'action':
|
|
493
|
+
result = agent.explainAction(options.targetId);
|
|
494
|
+
break;
|
|
495
|
+
case 'query':
|
|
496
|
+
result = agent.explainQuery(options.targetId);
|
|
497
|
+
break;
|
|
498
|
+
case 'workflow':
|
|
499
|
+
result = agent.explainWorkflow(options.targetId);
|
|
500
|
+
break;
|
|
501
|
+
case 'state':
|
|
502
|
+
result = agent.explainState(options.targetId);
|
|
503
|
+
break;
|
|
504
|
+
default:
|
|
505
|
+
throw new Error(`explain: unknown kind "${options.kind}" (expected action, query, workflow or state)`);
|
|
506
|
+
}
|
|
507
|
+
if (!result) {
|
|
508
|
+
process.exitCode = 1;
|
|
509
|
+
return `No ${options.kind} node "${options.targetId}" in this graph`;
|
|
510
|
+
}
|
|
511
|
+
return options.json ? JSON.stringify(result, null, 2) : formatExplanation(options.kind, result);
|
|
512
|
+
}
|
|
513
|
+
async function analyzeCommand(options) {
|
|
514
|
+
const agent = new AgentAPI(await loadGraph(options));
|
|
515
|
+
const summary = agent.explainGraph();
|
|
516
|
+
const capabilities = agent.analyzeCapabilities();
|
|
517
|
+
const native = agent.summarizeNativeOperations();
|
|
518
|
+
const authorization = agent.analyzeAuthorization();
|
|
519
|
+
if (options.json) {
|
|
520
|
+
return JSON.stringify({ summary, capabilities, native, unprotected: authorization.unprotected }, null, 2);
|
|
521
|
+
}
|
|
522
|
+
return [
|
|
523
|
+
`nodes by kind : ${Object.entries(summary.nodeCountsByKind).map(([k, v]) => `${k}=${v}`).join(', ') || 'none'}`,
|
|
524
|
+
`executable roots: ${summary.executableRoots.actions.length} client-invocable action(s), ${summary.executableRoots.workflows.length} workflow(s), ${summary.executableRoots.queries.length} quer(y/ies)`,
|
|
525
|
+
`security : ${summary.securityBoundaries.protectedActions} protected / ${summary.securityBoundaries.publicActions} public action(s); ${summary.securityBoundaries.protectedQueries} protected / ${summary.securityBoundaries.publicQueries} public quer(y/ies)`,
|
|
526
|
+
`native ops : ${native.count} (${native.opaqueCount} opaque — static analysis cannot see past them)`,
|
|
527
|
+
`capabilities : ${capabilities.requiredCapabilities.join(', ') || 'none required'}`,
|
|
528
|
+
`unprotected : ${authorization.unprotected.length} surface(s) with no explicit authorization boundary`,
|
|
529
|
+
].join('\n');
|
|
530
|
+
}
|
|
531
|
+
async function diffCommand(options) {
|
|
532
|
+
if (!options.against) {
|
|
533
|
+
throw new Error('diff needs a --against=<file> naming the graph to compare against');
|
|
534
|
+
}
|
|
535
|
+
const before = await loadGraphModule(options.against, options.againstExport);
|
|
536
|
+
const after = await loadGraph(options);
|
|
537
|
+
const diff = semanticDiff(before, after);
|
|
538
|
+
if (options.json) {
|
|
539
|
+
return JSON.stringify(diff, null, 2);
|
|
540
|
+
}
|
|
541
|
+
const lines = [
|
|
542
|
+
`${diff.entries.length} node change(s), ${diff.schema.entries.length} schema change(s)`,
|
|
543
|
+
...diff.entries.map((entry) => ` ${entry.changeKind[0].toUpperCase()} ${entry.nodeKind} ${entry.nodeId} [${entry.categories.join(', ')}]`),
|
|
544
|
+
...diff.schema.entries.map((entry) => ` ~ schema: ${entry.message}`),
|
|
545
|
+
'',
|
|
546
|
+
`semanticFingerprint changed : ${diff.compatibility.semanticFingerprintChanged}`,
|
|
547
|
+
`schemaFingerprint changed : ${diff.compatibility.schemaFingerprintChanged}`,
|
|
548
|
+
`server contract : ${diff.compatibility.serverContractBefore} -> ${diff.compatibility.serverContractAfter}`,
|
|
549
|
+
];
|
|
550
|
+
return lines.join('\n');
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* The one usage text, printed both for `--help` (exit 0) and for missing/malformed
|
|
554
|
+
* arguments (exit 1) — a fresh consumer must be able to discover every command and its
|
|
555
|
+
* `--json` / exit behavior from this alone, without repository access (spec16pt2 §63-64).
|
|
556
|
+
*/
|
|
557
|
+
const USAGE = [
|
|
558
|
+
'axiom — inspect, validate, explain and analyze an Axiom application graph.',
|
|
559
|
+
'',
|
|
560
|
+
'Usage:',
|
|
561
|
+
' axiom <build|inspect|serve> <modelFile> [--export=name] [--port=3000] [--store=state.db]',
|
|
562
|
+
' axiom validate <modelFile> [--export=name] [--json]',
|
|
563
|
+
' axiom schema status <modelFile> [--export=name]',
|
|
564
|
+
' axiom schema diff <modelFile> --against=<prevFile> [--export=name] [--against-export=name]',
|
|
565
|
+
' axiom migrate plan <modelFile> [--export=name] [--from=<version>]',
|
|
566
|
+
' axiom migrate <modelFile> [--export=name] [--from=<version>] [--approve=op1,op2] [--sqlite=<path>]',
|
|
567
|
+
' axiom migrate status <modelFile> --sqlite=<path>',
|
|
568
|
+
' axiom explain <action|query|workflow|state> <id> <modelFile> [--json]',
|
|
569
|
+
' axiom analyze <modelFile> [--json]',
|
|
570
|
+
' axiom diff <modelFile> --against=<prevFile> [--export=name] [--against-export=name] [--json]',
|
|
571
|
+
' axiom --help',
|
|
572
|
+
'',
|
|
573
|
+
'<modelFile> is a compiled (built) JavaScript module exporting an ApplicationGraph or a',
|
|
574
|
+
'function that builds one — see docs/AGENT_API.md and docs/AGENT_REFERENCE.md in',
|
|
575
|
+
'@cynodia/axiom for the semantic contract every command renders.',
|
|
576
|
+
'',
|
|
577
|
+
'Exit codes: 0 on success; nonzero on invalid input, an invalid graph, or a tooling failure.',
|
|
578
|
+
].join('\n');
|
|
579
|
+
async function main() {
|
|
580
|
+
const argv = process.argv.slice(2);
|
|
581
|
+
if (argv.includes('--help') || argv.includes('-h') || argv.length === 0) {
|
|
582
|
+
console.log(USAGE);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const options = parseArguments(argv);
|
|
586
|
+
if (!options) {
|
|
587
|
+
console.error(USAGE);
|
|
588
|
+
process.exitCode = 1;
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
switch (options.command) {
|
|
592
|
+
case 'schema status':
|
|
593
|
+
console.log(schemaStatus(await loadGraph(options)));
|
|
594
|
+
return;
|
|
595
|
+
case 'schema diff':
|
|
596
|
+
console.log(await schemaDiff(options));
|
|
597
|
+
return;
|
|
598
|
+
case 'migrate plan':
|
|
599
|
+
console.log(await migratePlan(options));
|
|
600
|
+
return;
|
|
601
|
+
case 'migrate status':
|
|
602
|
+
console.log(await migrateStatus(options));
|
|
603
|
+
return;
|
|
604
|
+
case 'migrate':
|
|
605
|
+
console.log(await migrateRun(options));
|
|
606
|
+
return;
|
|
607
|
+
case 'build':
|
|
608
|
+
await build(options);
|
|
609
|
+
return;
|
|
610
|
+
case 'inspect': {
|
|
611
|
+
console.log(inspect(await loadGraph(options)));
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
case 'validate': {
|
|
615
|
+
const result = validateGraph(await loadGraph(options));
|
|
616
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatValidation(result));
|
|
617
|
+
if (!result.valid) {
|
|
618
|
+
process.exitCode = 1;
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
case 'serve':
|
|
623
|
+
await serve(options);
|
|
624
|
+
return;
|
|
625
|
+
case 'explain':
|
|
626
|
+
console.log(await explainCommand(options));
|
|
627
|
+
return;
|
|
628
|
+
case 'analyze':
|
|
629
|
+
console.log(await analyzeCommand(options));
|
|
630
|
+
return;
|
|
631
|
+
case 'diff':
|
|
632
|
+
console.log(await diffCommand(options));
|
|
633
|
+
return;
|
|
634
|
+
default:
|
|
635
|
+
console.error(`Unknown command: ${options.command}`);
|
|
636
|
+
process.exitCode = 1;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
main().catch((error) => {
|
|
640
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
641
|
+
process.exitCode = 1;
|
|
642
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cynodia/axiom-cli",
|
|
3
|
+
"version": "0.16.0-alpha.2",
|
|
4
|
+
"description": "Command line tools for inspecting, validating and serving Axiom applications.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "AskTech AS",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/cynodia/axiom.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/cynodia/axiom",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/cynodia/axiom/issues"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/**/*.js",
|
|
21
|
+
"dist/**/*.d.ts",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"axiom": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@cynodia/axiom-core": "0.16.0-alpha.2",
|
|
38
|
+
"@cynodia/axiom-compiler": "0.16.0-alpha.2",
|
|
39
|
+
"@cynodia/axiom-server": "0.16.0-alpha.2",
|
|
40
|
+
"@cynodia/axiom-agent-api": "0.16.0-alpha.2"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -b tsconfig.json"
|
|
44
|
+
}
|
|
45
|
+
}
|