@m6d/cortex-cli 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +193 -128
  2. package/package.json +16 -5
  3. package/src/cli.ts +19 -12
  4. package/src/commands/apply.ts +186 -0
  5. package/src/commands/{cc.ts → management.ts} +173 -112
  6. package/src/commands/new.ts +83 -84
  7. package/src/commands/setup.ts +610 -0
  8. package/src/contracts/charts/chart-spec.ts +1 -1
  9. package/src/contracts/client-tools/index.ts +3 -3
  10. package/src/contracts/{runtime → datasets}/datasets.ts +7 -42
  11. package/src/contracts/datasets/index.ts +3 -0
  12. package/src/contracts/egress-policy/index.ts +16 -0
  13. package/src/contracts/{egress → egress-policy}/scrub.ts +34 -5
  14. package/src/contracts/{egress → egress-policy}/url.ts +22 -9
  15. package/src/contracts/errors/index.ts +38 -0
  16. package/src/contracts/json/index.ts +5 -0
  17. package/src/contracts/manifest/index.ts +973 -0
  18. package/src/contracts/manifest-file/index.ts +16 -0
  19. package/src/contracts/markdown/index.ts +1 -1
  20. package/src/contracts/pattern/index.ts +276 -0
  21. package/src/contracts/server/index.ts +349 -0
  22. package/src/contracts/server/reserved-names.fixtures.json +18 -0
  23. package/src/contracts/triggers/index.ts +112 -0
  24. package/src/contracts/wire/index.ts +67 -33
  25. package/src/{cc → management}/client.ts +33 -7
  26. package/src/{cc → management}/flags.ts +2 -1
  27. package/src/{cc → management}/profiles.ts +10 -8
  28. package/src/scaffold/files.ts +448 -284
  29. package/src/scaffold/ports.ts +12 -24
  30. package/src/scaffold/templates/cspell.json.txt +41 -0
  31. package/src/scaffold/templates/datasets.ts.txt +52 -0
  32. package/src/scaffold/templates/dockerignore.txt +6 -0
  33. package/src/scaffold/templates/fallowrc.jsonc.txt +8 -12
  34. package/src/scaffold/templates/gitignore.txt +1 -1
  35. package/src/scaffold/templates/server.ts.txt +40 -0
  36. package/src/scaffold/templates/setup.ts.txt +44 -0
  37. package/src/ui/prompts.ts +24 -38
  38. package/src/ui/report.ts +5 -15
  39. package/tsconfig.json +2 -3
  40. package/src/commands/graph.ts +0 -228
  41. package/src/commands/swagger.ts +0 -247
  42. package/src/config/load.ts +0 -122
  43. package/src/config/validate.ts +0 -46
  44. package/src/contracts/credentials/index.ts +0 -183
  45. package/src/contracts/egress/index.ts +0 -120
  46. package/src/contracts/graph/clients/embed.ts +0 -62
  47. package/src/contracts/graph/clients/neo4j.ts +0 -85
  48. package/src/contracts/graph/helpers.ts +0 -55
  49. package/src/contracts/graph/index.ts +0 -30
  50. package/src/contracts/graph/schema.ts +0 -65
  51. package/src/contracts/graph/types.ts +0 -125
  52. package/src/contracts/rich-text/index.ts +0 -220
  53. package/src/contracts/runtime/api.ts +0 -345
  54. package/src/contracts/runtime/auth.ts +0 -41
  55. package/src/contracts/runtime/grammar.ts +0 -169
  56. package/src/contracts/runtime/index.ts +0 -15
  57. package/src/contracts/runtime/models.ts +0 -50
  58. package/src/contracts/runtime/signature.ts +0 -255
  59. package/src/contracts/runtime/sync.ts +0 -196
  60. package/src/contracts/runtime/triggers.ts +0 -267
  61. package/src/graph/expand-domains.ts +0 -286
  62. package/src/graph/generate-cypher.ts +0 -201
  63. package/src/graph/seed.ts +0 -199
  64. package/src/graph/validate.ts +0 -78
  65. package/src/scaffold/features.ts +0 -190
  66. package/src/scaffold/templates/domains-README.md.txt +0 -16
  67. package/src/scaffold/templates/index.ts.txt +0 -18
  68. package/src/swagger/extract-endpoints.ts +0 -269
  69. package/src/swagger/generated-block.ts +0 -74
  70. package/src/swagger/openapi-schema.ts +0 -168
  71. package/src/swagger/parse-response.ts +0 -88
  72. package/src/swagger/types.ts +0 -25
  73. /package/src/contracts/{runtime → datasets}/dataset-sql-guide.ts +0 -0
  74. /package/src/contracts/{runtime → datasets}/dataset-sql.ts +0 -0
  75. /package/src/contracts/{runtime → datasets}/datasets.fixtures.json +0 -0
  76. /package/src/{cc → management}/output.ts +0 -0
@@ -1,183 +0,0 @@
1
- import { z } from "zod";
2
- import { canCarrySecret } from "../egress/url";
3
-
4
- export { canCarrySecret };
5
-
6
- /*
7
- * A credential is a value Cortex presents to an upstream: one it holds
8
- * (`static`) or one it acquires (`oauth2-client-credentials`, RFC 6749 §4.4).
9
- * The definition is the same on both sides; the Control Center stores it,
10
- * a code config declares it, and each side resolves it to a bearer through
11
- * the helper and cache below with its own egress-guarded fetch.
12
- */
13
-
14
- export const CREDENTIAL_KINDS = ["static", "oauth2-client-credentials"] as const;
15
- export type CredentialKind = (typeof CREDENTIAL_KINDS)[number];
16
-
17
- export const credentialDefinitionSchema = z.discriminatedUnion("kind", [
18
- z.object({
19
- kind: z.literal("static"),
20
- value: z.string().min(1),
21
- }),
22
- z.object({
23
- kind: z.literal("oauth2-client-credentials"),
24
- /** The token endpoint; a client secret travels over TLS, to loopback, or to a host the egress policy admits. */
25
- tokenUrl: z
26
- .url()
27
- .refine(
28
- (url) => canCarrySecret(url),
29
- "token URL must be https, or http to loopback or to a host the egress policy admits",
30
- ),
31
- clientId: z.string().min(1),
32
- clientSecret: z.string().min(1),
33
- scope: z.string().min(1).optional(),
34
- }),
35
- ]);
36
- export type CredentialDefinition = z.infer<typeof credentialDefinitionSchema>;
37
- export type OAuth2ClientCredentials = Extract<
38
- CredentialDefinition,
39
- { kind: "oauth2-client-credentials" }
40
- >;
41
-
42
- /** One POST to the token endpoint through the caller's guarded fetch; `null` when the transport failed. */
43
- export type TokenPost = (
44
- url: string,
45
- init: { headers: Record<string, string>; body: string },
46
- ) => Promise<{ status: number; body: string } | null>;
47
-
48
- /** A bearer and when it stops being one; `null` when the issuer did not say. */
49
- export type AcquiredToken = { token: string; expiresAt: number | null };
50
-
51
- export type AcquireResult =
52
- | { ok: true; value: AcquiredToken }
53
- | {
54
- ok: false;
55
- error:
56
- | "token_url_insecure"
57
- | "token_request_failed"
58
- | "token_rejected"
59
- | "token_response_invalid";
60
- };
61
-
62
- const tokenResponseSchema = z.object({
63
- access_token: z.string().min(1),
64
- token_type: z.string().optional(),
65
- // Some issuers (Azure AD v1 among them) send the lifetime as a string.
66
- expires_in: z
67
- .union([z.number(), z.string().regex(/^\d+$/).transform(Number)])
68
- .pipe(z.number().nonnegative())
69
- .optional(),
70
- });
71
-
72
- /**
73
- * The client credentials grant: a form-encoded POST with the client
74
- * authenticated by `client_secret_basic` (RFC 6749 §2.3.1, each half
75
- * form-encoded before base64). The endpoint's body never leaves this
76
- * function: a failure is one of three literals, since the body may echo
77
- * the client secret or carry the issuer's own error detail.
78
- */
79
- export async function acquireOAuth2ClientCredentials(
80
- definition: OAuth2ClientCredentials,
81
- post: TokenPost,
82
- ) {
83
- // Judged again here, not only when the definition was saved: the policy
84
- // may have tightened since, and a secret never travels where it may not.
85
- if (!canCarrySecret(definition.tokenUrl)) {
86
- return { ok: false, error: "token_url_insecure" } satisfies AcquireResult;
87
- }
88
- const basic = Buffer.from(
89
- `${encodeURIComponent(definition.clientId)}:${encodeURIComponent(definition.clientSecret)}`,
90
- ).toString("base64");
91
- const answer = await post(definition.tokenUrl, {
92
- headers: {
93
- Authorization: `Basic ${basic}`,
94
- "Content-Type": "application/x-www-form-urlencoded",
95
- Accept: "application/json",
96
- },
97
- body: new URLSearchParams({
98
- grant_type: "client_credentials",
99
- ...(definition.scope ? { scope: definition.scope } : {}),
100
- }).toString(),
101
- });
102
- if (!answer) return { ok: false, error: "token_request_failed" } satisfies AcquireResult;
103
- if (answer.status < 200 || answer.status >= 300) {
104
- return { ok: false, error: "token_rejected" } satisfies AcquireResult;
105
- }
106
- let parsed: z.ZodSafeParseResult<z.infer<typeof tokenResponseSchema>>;
107
- try {
108
- parsed = tokenResponseSchema.safeParse(JSON.parse(answer.body));
109
- } catch {
110
- return { ok: false, error: "token_response_invalid" } satisfies AcquireResult;
111
- }
112
- if (!parsed.success || (parsed.data.token_type ?? "bearer").toLowerCase() !== "bearer") {
113
- return { ok: false, error: "token_response_invalid" } satisfies AcquireResult;
114
- }
115
- const issuedAt = Date.now();
116
- return {
117
- ok: true as const,
118
- value: {
119
- token: parsed.data.access_token,
120
- expiresAt:
121
- parsed.data.expires_in === undefined
122
- ? null
123
- : issuedAt + parsed.data.expires_in * 1000,
124
- },
125
- } satisfies AcquireResult;
126
- }
127
-
128
- /** A token is not reused inside this margin of its expiry: the call it is presented on still has to complete. */
129
- const EXPIRY_MARGIN_MS = 30_000;
130
-
131
- /** Equal definitions share one key; a rotated secret is another definition and another token. Kept in memory only, like the definitions themselves. */
132
- function keyOf(definition: CredentialDefinition) {
133
- return JSON.stringify(definition);
134
- }
135
-
136
- /**
137
- * Acquired tokens by definition, reused until their expiry margin. Concurrent
138
- * acquisitions of one definition share a single request; a token the issuer
139
- * gave no expiry for is answered but not kept, since nothing says when it
140
- * stops working. `invalidate` is for the caller that saw a 401: the next
141
- * resolve acquires again. Pure, so the console's client bundle may import
142
- * the schemas beside it.
143
- */
144
- export function createCredentialCache(now: () => number = Date.now) {
145
- const tokens = new Map<string, AcquiredToken>();
146
- const inFlight = new Map<string, Promise<AcquireResult>>();
147
-
148
- return {
149
- async get(definition: CredentialDefinition, acquire: () => Promise<AcquireResult>) {
150
- const key = keyOf(definition);
151
- const cached = tokens.get(key);
152
- if (
153
- cached &&
154
- cached.expiresAt !== null &&
155
- cached.expiresAt - EXPIRY_MARGIN_MS > now()
156
- ) {
157
- return { ok: true, value: cached } satisfies AcquireResult;
158
- }
159
- tokens.delete(key);
160
- const pending = inFlight.get(key);
161
- if (pending) return pending;
162
- const acquiring = acquire()
163
- .then((result) => {
164
- if (
165
- result.ok &&
166
- result.value.expiresAt !== null &&
167
- result.value.expiresAt - EXPIRY_MARGIN_MS > now()
168
- ) {
169
- tokens.set(key, result.value);
170
- }
171
- return result;
172
- })
173
- .finally(() => inFlight.delete(key));
174
- inFlight.set(key, acquiring);
175
- return acquiring;
176
- },
177
- invalidate(definition: CredentialDefinition) {
178
- tokens.delete(keyOf(definition));
179
- },
180
- };
181
- }
182
-
183
- export type CredentialCache = ReturnType<typeof createCredentialCache>;
@@ -1,120 +0,0 @@
1
- import { lookup } from "node:dns/promises";
2
- import type { LookupFunction } from "node:net";
3
- import { Agent } from "undici";
4
- import { isEgressAllowedHost, isPrivateAddress, normalizeHost } from "./url";
5
-
6
- /*
7
- * The outbound network policy for URLs a tenant controls (tool endpoints,
8
- * per-agent JWKS URIs): private, loopback, link-local, and metadata ranges are
9
- * refused, and DNS is pinned so a rebind cannot slip a private address past
10
- * the check, and credentials an answer echoes back are redacted from it.
11
- * Shared by cc and the server, vendored like the rest of this package.
12
- */
13
-
14
- export { credentialForms, scrubCredentialStrings } from "./scrub";
15
-
16
- /**
17
- * Whether a host may be dialed at these addresses: any address when the
18
- * policy admits the host (`EGRESS_ALLOWED_HOSTS`), else only public ones.
19
- */
20
- function isDialable(hostname: string, addresses: string[]) {
21
- return isEgressAllowedHost(hostname) || !addresses.some(isPrivateAddress);
22
- }
23
-
24
- export { canCarrySecret, egressAllowedHosts } from "./url";
25
-
26
- export function egressError() {
27
- const error: NodeJS.ErrnoException = new Error("egress blocked");
28
- error.code = "EGRESS_BLOCKED";
29
- return error;
30
- }
31
-
32
- /** True when `error` or any of its causes is the egress refusal. */
33
- export function isEgressError(error: unknown) {
34
- for (let current = error; current instanceof Error; current = current.cause) {
35
- if ((current as NodeJS.ErrnoException).code === "EGRESS_BLOCKED") return true;
36
- }
37
- return false;
38
- }
39
-
40
- /**
41
- * Re-resolves the hostname and re-validates every candidate on every connection
42
- * attempt, handing the socket only vetted public addresses. The address dialed
43
- * is the exact one just checked, so a nameserver cannot return a public IP to
44
- * the check and a private one to the connect. Server name / Host header stay
45
- * the original hostname.
46
- */
47
- const pinnedLookup: LookupFunction = (hostname, options, callback) => {
48
- lookup(hostname, { all: true, verbatim: true }).then(
49
- (addresses) => {
50
- const first = addresses[0];
51
- if (
52
- !first ||
53
- !isDialable(
54
- hostname,
55
- addresses.map((address) => address.address),
56
- )
57
- ) {
58
- callback(egressError(), "", 0);
59
- return;
60
- }
61
- if (options.all) {
62
- callback(
63
- null,
64
- addresses.map((address) => ({
65
- address: address.address,
66
- family: address.family,
67
- })),
68
- );
69
- return;
70
- }
71
- callback(null, first.address, first.family);
72
- },
73
- (error: unknown) =>
74
- callback(error instanceof Error ? error : new Error("lookup failed"), "", 0),
75
- );
76
- };
77
-
78
- /**
79
- * Checks a URL before any socket opens: http(s) only, no embedded credentials,
80
- * an optional per-call restriction to `allowedHostnames`, and the host dialable
81
- * (every resolved address public, or the host admitted by the policy). Returns
82
- * the bare hostname on success. `pinnedLookup` re-checks at connect time; this
83
- * is the cheap, clean refusal for a statically bad target.
84
- */
85
- export async function checkEgressTarget(target: URL, allowedHostnames?: string[]) {
86
- // Resolved as written, root dot included, so an absolute name is never search-suffixed; normalized only to compare.
87
- const hostname = target.hostname.replace(/^\[(.*)]$/, "$1");
88
-
89
- if (!["http:", "https:"].includes(target.protocol) || target.username || target.password) {
90
- return false;
91
- }
92
- if (
93
- allowedHostnames &&
94
- !allowedHostnames.map(normalizeHost).includes(normalizeHost(hostname))
95
- ) {
96
- return false;
97
- }
98
-
99
- const preflight = await lookup(hostname, { all: true, verbatim: true });
100
- return (
101
- preflight.length > 0 &&
102
- isDialable(
103
- hostname,
104
- preflight.map((address) => address.address),
105
- )
106
- );
107
- }
108
-
109
- /**
110
- * An undici dispatcher whose every connection resolves through `pinnedLookup`.
111
- * Call the returned `release` once the response has been read; Bun's undici
112
- * Agent may lack `close`, so release tolerates its absence.
113
- */
114
- export function guardedDispatcher() {
115
- const dispatcher = new Agent({ connect: { lookup: pinnedLookup } });
116
- const release = () => {
117
- if (typeof dispatcher.close === "function") void dispatcher.close().catch(() => undefined);
118
- };
119
- return { dispatcher, release };
120
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * Embeddings, which both halves of the graph need: `@m6d/cortex-cli` embeds every
3
- * concept description as it seeds, `@m6d/cortex-server` embeds every prompt as it
4
- * resolves. Two embedders would mean two vector spaces, so there is one. A bare
5
- * `fetch` to the OpenAI-compatible `/embeddings` call keeps this package
6
- * dependency-free. Everything under `graph/` compiles into an Angular library
7
- * too, so it is written to the oldest target any consumer builds against.
8
- */
9
-
10
- /** Everything cortex needs to reach one OpenAI-compatible embeddings endpoint. */
11
- export type EmbeddingProviderConfig = {
12
- baseURL: string;
13
- apiKey: string;
14
- modelName: string;
15
- };
16
-
17
- /** Turns a batch of strings into their vectors, in the order given. */
18
- export type EmbedFn = (values: string[]) => Promise<number[][]>;
19
-
20
- /**
21
- * The most inputs one embeddings request carries. Text Embeddings Inference
22
- * refuses more than 32 by default; providers that allow larger batches just
23
- * take more requests.
24
- */
25
- export const EMBEDDING_MAX_BATCH_SIZE = 32;
26
-
27
- export function createEmbedder(config: EmbeddingProviderConfig) {
28
- const url = `${config.baseURL.replace(/\/+$/, "")}/embeddings`;
29
-
30
- async function embedBatch(values: string[]) {
31
- const response = await fetch(url, {
32
- method: "POST",
33
- headers: {
34
- "Content-Type": "application/json",
35
- Authorization: `Bearer ${config.apiKey}`,
36
- },
37
- body: JSON.stringify({ model: config.modelName, input: values }),
38
- });
39
-
40
- if (!response.ok) {
41
- throw new Error(
42
- `Embedding request failed (${response.status}): ${await response.text()}`,
43
- );
44
- }
45
-
46
- // The API is free to return entries out of order; `index` is authoritative.
47
- const body = (await response.json()) as { data: { index: number; embedding: number[] }[] };
48
- return [...body.data]
49
- .sort((left, right) => left.index - right.index)
50
- .map((entry) => entry.embedding);
51
- }
52
-
53
- return async function embed(values: string[]) {
54
- const embeddings: number[][] = [];
55
- for (let index = 0; index < values.length; index += EMBEDDING_MAX_BATCH_SIZE) {
56
- embeddings.push(
57
- ...(await embedBatch(values.slice(index, index + EMBEDDING_MAX_BATCH_SIZE))),
58
- );
59
- }
60
- return embeddings;
61
- };
62
- }
@@ -1,85 +0,0 @@
1
- /**
2
- * Neo4j over its HTTP transaction endpoint, shared by the graph's writer (the
3
- * CLI seeder) and its reader (the server resolver). Configuration is explicit
4
- * and the transport is `fetch`, so this package stays dependency-free.
5
- */
6
-
7
- import type { EmbedFn } from "./embed";
8
-
9
- export type Neo4jConfig = {
10
- url: string;
11
- user: string;
12
- password: string;
13
- };
14
-
15
- export type Neo4jRow = Record<string, unknown>;
16
-
17
- export type Neo4jClient = {
18
- /** Runs one statement and returns its rows as column-keyed objects. Throws `Neo4jQueryError` on any failure. */
19
- query(cypher: string, parameters?: Record<string, unknown>): Promise<Neo4jRow[]>;
20
- };
21
-
22
- export class Neo4jQueryError extends Error {
23
- public constructor(
24
- message: string,
25
- /** Neo4j's error code (`Neo.ClientError.…`), or the HTTP status when the request itself failed. */
26
- public readonly code: string,
27
- ) {
28
- super(message);
29
- this.name = "Neo4jQueryError";
30
- }
31
- }
32
-
33
- /**
34
- * Parameters whose key starts with `#` are embedded before the statement runs:
35
- * `{ "#embedding": "text" }` becomes `{ embedding: number[] }`.
36
- */
37
- export function createNeo4jClient(config: Neo4jConfig, embed?: EmbedFn) {
38
- async function query(cypher: string, parameters: Record<string, unknown> = {}) {
39
- const embeddedKeys = Object.keys(parameters).filter((key) => key.startsWith("#"));
40
- if (embeddedKeys.length) {
41
- if (!embed) throw new Error("An embedder is required when using # parameters");
42
- const embeddings = await embed(embeddedKeys.map((key) => parameters[key] as string));
43
- embeddedKeys.forEach((key, index) => {
44
- delete parameters[key];
45
- parameters[key.slice(1)] = embeddings[index]!;
46
- });
47
- }
48
-
49
- const response = await fetch(`${config.url}/db/neo4j/tx/commit`, {
50
- method: "POST",
51
- headers: {
52
- "Content-Type": "application/json",
53
- Authorization: `Basic ${btoa(`${config.user}:${config.password}`)}`,
54
- },
55
- body: JSON.stringify({ statements: [{ statement: cypher, parameters }] }),
56
- });
57
- if (!response.ok) {
58
- throw new Neo4jQueryError(
59
- `Neo4j request failed with status ${response.status}`,
60
- String(response.status),
61
- );
62
- }
63
-
64
- const result = (await response.json()) as {
65
- errors?: { code?: string; message: string }[];
66
- results: { columns: string[]; data: { row: unknown[] }[] }[];
67
- };
68
- if (result.errors?.length) {
69
- throw new Neo4jQueryError(
70
- result.errors.map((error) => error.message).join("; "),
71
- result.errors[0]!.code ?? "unknown",
72
- );
73
- }
74
-
75
- const queryResult = result.results[0];
76
- if (!queryResult) return [];
77
- return queryResult.data.map((entry) =>
78
- Object.fromEntries(
79
- queryResult.columns.map((column, index) => [column, entry.row[index]]),
80
- ),
81
- );
82
- }
83
-
84
- return { query } satisfies Neo4jClient;
85
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Helper functions for building knowledge graph seed data. Projects author
3
- * domains with these; `@m6d/cortex-server` re-exports them so authoring needs
4
- * exactly one import path.
5
- */
6
-
7
- import type {
8
- ConceptDef,
9
- DomainDef,
10
- EndpointDef,
11
- EndpointInput,
12
- RuleDef,
13
- ServiceDef,
14
- } from "./types";
15
-
16
- export function defineConcept(def: Omit<ConceptDef, "__brand">) {
17
- return { __brand: "concept" as const, ...def };
18
- }
19
-
20
- export function defineRule(def: Omit<RuleDef, "__brand">) {
21
- return { __brand: "rule" as const, ...def };
22
- }
23
-
24
- export function defineService(def: Omit<ServiceDef, "__brand">) {
25
- return { __brand: "service" as const, ...def };
26
- }
27
-
28
- export function defineDomain(def: Omit<DomainDef, "__brand">) {
29
- return { __brand: "domain" as const, ...def };
30
- }
31
-
32
- export function defineEndpoint(input: EndpointInput) {
33
- const generated = input.autoGenerated;
34
-
35
- return {
36
- __brand: "endpoint" as const,
37
- name: input.name,
38
- description: input.description ?? input.name,
39
- path: input.path,
40
- method: input.method,
41
- propertiesDescriptions: input.propertiesDescriptions ?? {},
42
- params: generated ? [...generated.params] : [],
43
- body: generated ? [...generated.body] : [],
44
- response: generated ? [...generated.response] : [],
45
- successStatus: generated?.successStatus ?? 200,
46
- errorStatuses: generated ? [...generated.errorStatuses] : [],
47
- responseKind: generated?.responseKind ?? "object",
48
- queries: input.queries,
49
- mutates: input.mutates,
50
- returns: input.returns,
51
- dependsOn: input.dependsOn,
52
- governedBy: input.governedBy,
53
- metadata: input.metadata,
54
- } satisfies EndpointDef;
55
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * The graph contract — the knowledge-graph vocabulary, authoring types and
3
- * clients that `@m6d/cortex-cli` writes with and `@m6d/cortex-server` reads
4
- * with. `@m6d/cortex-server` re-exports all of it, so a project authoring
5
- * domains still has exactly one import path.
6
- */
7
-
8
- export type { GraphSchema } from "./schema";
9
- export { GRAPH_SCHEMA, GRAPH_SCHEMA_VERSION } from "./schema";
10
-
11
- export type {
12
- EndpointScalarType,
13
- EndpointProperty,
14
- ResponseKind,
15
- AutoGenerated,
16
- ConceptDef,
17
- EndpointDef,
18
- EndpointInput,
19
- ServiceDef,
20
- RuleDef,
21
- DomainDef,
22
- } from "./types";
23
-
24
- export { defineConcept, defineRule, defineService, defineDomain, defineEndpoint } from "./helpers";
25
-
26
- export type { Neo4jConfig, Neo4jClient, Neo4jRow } from "./clients/neo4j";
27
- export { createNeo4jClient, Neo4jQueryError } from "./clients/neo4j";
28
-
29
- export type { EmbedFn, EmbeddingProviderConfig } from "./clients/embed";
30
- export { createEmbedder, EMBEDDING_MAX_BATCH_SIZE } from "./clients/embed";
@@ -1,65 +0,0 @@
1
- /**
2
- * The contract between the graph's writer and its reader.
3
- *
4
- * Seeding lives in `@m6d/cortex-cli` and resolution lives in `@m6d/cortex-server`,
5
- * two packages a project installs and upgrades separately. The labels, relationship
6
- * types, property names and index name they agree on are this module, here, so
7
- * neither side owns the vocabulary the other has to match.
8
- *
9
- * A mismatch between the two is invisible at runtime: the resolver's Cypher
10
- * matches nothing and retrieval comes back empty rather than failing. That is what
11
- * `GRAPH_SCHEMA_VERSION` is for — bump it on any change to the shape below. The
12
- * CLI stamps the version it wrote onto the graph and the server checks that stamp
13
- * on its first resolve, so skew surfaces as an error instead of as silence.
14
- */
15
-
16
- export const GRAPH_SCHEMA_VERSION = 1;
17
-
18
- export const GRAPH_SCHEMA = {
19
- label: {
20
- domain: "Domain",
21
- concept: "Concept",
22
- endpoint: "Endpoint",
23
- service: "Service",
24
- rule: "Rule",
25
- /** Stamped with the version above, so a stale graph is detectable later. */
26
- marker: "CortexGraph",
27
- },
28
- rel: {
29
- belongsTo: "BELONGS_TO",
30
- specializes: "SPECIALIZES",
31
- queriedVia: "QUERIED_VIA",
32
- mutatedVia: "MUTATED_VIA",
33
- returns: "RETURNS",
34
- dependsOn: "DEPENDS_ON",
35
- governs: "GOVERNS",
36
- requestedVia: "REQUESTED_VIA",
37
- },
38
- prop: {
39
- name: "name",
40
- description: "description",
41
- aliases: "aliases",
42
- metadata: "metadata",
43
- path: "path",
44
- method: "method",
45
- params: "params",
46
- body: "body",
47
- response: "response",
48
- propertiesDescriptions: "propertiesDescriptions",
49
- successStatus: "successStatus",
50
- errorStatuses: "errorStatuses",
51
- builtInId: "builtInId",
52
- embedding: "embedding",
53
- /** On a RETURNS edge: which part of the response carries the concept. */
54
- field: "field",
55
- /** On a DEPENDS_ON edge. */
56
- paramName: "paramName",
57
- fromField: "fromField",
58
- /** On the marker node. */
59
- version: "version",
60
- },
61
- /** Created by the CLI's seeder, queried by name in the resolver's first hop. */
62
- conceptEmbeddingsIndex: "concept_embeddings",
63
- } as const;
64
-
65
- export type GraphSchema = typeof GRAPH_SCHEMA;