@m6d/cortex-cli 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/package.json +3 -3
- package/src/commands/new.ts +4 -4
- package/src/commands/setup.ts +19 -10
- package/src/contracts/datasets/dataset-sql.ts +1 -1
- package/src/contracts/management/index.ts +13 -1
- package/src/contracts/management/procedures.generated.ts +401 -216
- package/src/contracts/manifest/index.ts +130 -137
- package/src/contracts/server/index.ts +33 -9
- package/src/contracts/triggers/index.ts +9 -9
- package/src/contracts/wire/index.ts +11 -0
- package/src/management/client.ts +2 -0
- package/src/scaffold/files.ts +41 -37
- package/src/scaffold/ports.ts +5 -4
- package/src/scaffold/templates/cspell.json.txt +1 -1
- package/src/scaffold/templates/oxlintrc.json.txt +67 -0
- package/src/scaffold/templates/setup.ts.txt +1 -0
- package/src/scaffold/templates/eslint.config.mjs.txt +0 -20
package/src/scaffold/files.ts
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
* Every file `cortex new` writes, as one pure function. Files with nothing to
|
|
3
3
|
* fill in are verbatim text under templates/; the ones that vary with the
|
|
4
4
|
* project are assembled here. The tooling configs are inlined rather than
|
|
5
|
-
* pulled from `@m6d/
|
|
6
|
-
* those are `private: true` at `0.0.0`
|
|
7
|
-
*
|
|
5
|
+
* pulled from `@m6d/prettier-config`, `@m6d/tsconfig` and the root
|
|
6
|
+
* `.oxlintrc.json`: those are `private: true` at `0.0.0` (the lint config
|
|
7
|
+
* loads `@m6d/oxlint-plugin`) and resolvable only through this workspace, and
|
|
8
|
+
* a standalone scaffold has no workspace.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
import { NAME_MAX_LENGTH } from "@cortex/contracts/manifest";
|
|
11
|
-
|
|
12
11
|
import pkg from "../../package.json";
|
|
13
12
|
import serverTs from "@/scaffold/templates/server.ts.txt";
|
|
14
13
|
import setupTs from "@/scaffold/templates/setup.ts.txt";
|
|
@@ -16,11 +15,11 @@ import datasetsTs from "@/scaffold/templates/datasets.ts.txt";
|
|
|
16
15
|
import tsconfigJson from "@/scaffold/templates/tsconfig.json.txt";
|
|
17
16
|
import gitignoreText from "@/scaffold/templates/gitignore.txt";
|
|
18
17
|
import dockerignoreText from "@/scaffold/templates/dockerignore.txt";
|
|
19
|
-
import
|
|
18
|
+
import oxlintrc from "@/scaffold/templates/oxlintrc.json.txt";
|
|
20
19
|
import prettierConfig from "@/scaffold/templates/prettier.config.mjs.txt";
|
|
21
20
|
import fallowrc from "@/scaffold/templates/fallowrc.jsonc.txt";
|
|
22
|
-
// Project jargon is the project's to add. Only
|
|
23
|
-
// package.json
|
|
21
|
+
// Project jargon is the project's to add. Only the tool names its own
|
|
22
|
+
// package.json carries are seeded: a stranger's vocabulary is noise in a repo.
|
|
24
23
|
import cspellJson from "@/scaffold/templates/cspell.json.txt";
|
|
25
24
|
import { applyPorts, type Ports } from "@/scaffold/ports";
|
|
26
25
|
|
|
@@ -29,7 +28,7 @@ import { applyPorts, type Ports } from "@/scaffold/ports";
|
|
|
29
28
|
* `@m6d/cortex` range below are stamped by scripts/sync-package-versions.ts
|
|
30
29
|
* before the CLI publishes, so a scaffold pins what was current then.
|
|
31
30
|
*/
|
|
32
|
-
const CORTEX_VERSION = "0.
|
|
31
|
+
const CORTEX_VERSION = "0.20.0";
|
|
33
32
|
|
|
34
33
|
/** This machine as Cortex's containers name it: the one private host the stack's egress policy admits. */
|
|
35
34
|
export const DOCKER_HOST = "host.docker.internal";
|
|
@@ -39,9 +38,9 @@ const SERVER_URL = `http://${DOCKER_HOST}:{{port.server}}`;
|
|
|
39
38
|
|
|
40
39
|
/**
|
|
41
40
|
* The agent the manifest declares and the README sends you to chat with.
|
|
42
|
-
* Not `assistant`: Cortex holds that
|
|
41
|
+
* Not `assistant`: Cortex holds that name for its console's own assistant.
|
|
43
42
|
*/
|
|
44
|
-
export const
|
|
43
|
+
export const AGENT_NAME = "helper";
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* The embedding model the stack asks for, and the dimension the image's
|
|
@@ -54,18 +53,16 @@ export const EMBEDDING = { model: "text-embedding-3-small", dimension: 768 } as
|
|
|
54
53
|
const DEV_DEPENDENCIES = {
|
|
55
54
|
// `cortex apply`, `cortex setup` and the management commands, at the release that wrote the project.
|
|
56
55
|
"@m6d/cortex-cli": `~${pkg.version}`,
|
|
57
|
-
"@types/bun": "1.
|
|
56
|
+
"@types/bun": "1.4.4",
|
|
58
57
|
// The two runners of `bun run dev` and `bun run dev:agent`.
|
|
59
58
|
concurrently: "^10.0.6",
|
|
60
59
|
cspell: "^9.6.4",
|
|
61
|
-
eslint: "^9",
|
|
62
|
-
"eslint-config-prettier": "^10.1.8",
|
|
63
|
-
"eslint-plugin-prettier": "^5.5.5",
|
|
64
60
|
fallow: "^3.9.1",
|
|
65
61
|
mprocs: "^0.9.6",
|
|
62
|
+
oxlint: "^1.90.0",
|
|
63
|
+
"oxlint-tsgolint": "^7.0.2005",
|
|
66
64
|
prettier: "^3.8.1",
|
|
67
|
-
typescript: "
|
|
68
|
-
"typescript-eslint": "^8.65.0",
|
|
65
|
+
typescript: "^7.0.2",
|
|
69
66
|
};
|
|
70
67
|
|
|
71
68
|
/**
|
|
@@ -112,7 +109,7 @@ function runAll(runner: "mprocs" | "concurrently", processes: Record<string, str
|
|
|
112
109
|
return `${runner} --names ${Object.keys(processes).join(",")} ${commands.join(" ")}`;
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
/** The name as the manifest `id`
|
|
112
|
+
/** The name as the manifest `id` and the server's name take it: lowercase words and hyphens. */
|
|
116
113
|
export function manifestIdOf(name: string) {
|
|
117
114
|
return name
|
|
118
115
|
.replace(/[^a-z0-9]+/g, "-")
|
|
@@ -145,12 +142,12 @@ function packageJson({ name, datasets, setup }: Project) {
|
|
|
145
142
|
postinstall: "bun run --if-present setup.ts",
|
|
146
143
|
format: "prettier --write .",
|
|
147
144
|
"format:check": "prettier --check .",
|
|
148
|
-
lint: "
|
|
145
|
+
lint: "oxlint",
|
|
149
146
|
spellcheck: 'cspell "**/*.{ts,mjs,json,md}"',
|
|
150
147
|
verify: "bun run format:check && bun run lint && bun run spellcheck && bun run check && bun run audit",
|
|
151
148
|
},
|
|
152
149
|
dependencies: {
|
|
153
|
-
"@m6d/cortex": "~0.
|
|
150
|
+
"@m6d/cortex": "~0.8.0",
|
|
154
151
|
// drizzle 1.0 is in beta; this is the build `@m6d/cortex/drizzle` is tested against.
|
|
155
152
|
...(datasets ? { "drizzle-orm": "1.0.0-beta.15-859cf75" } : {}),
|
|
156
153
|
hono: "^4.12.3",
|
|
@@ -219,8 +216,8 @@ export default defineManifest({
|
|
|
219
216
|
id: ${JSON.stringify(manifestIdOf(name))},
|
|
220
217
|
agents: [
|
|
221
218
|
{
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
name: "${AGENT_NAME}",
|
|
220
|
+
displayName: { en: "Helper" },
|
|
224
221
|
server,
|
|
225
222
|
prompt: "You are a helpful assistant. Address the user as {{userName}}.",
|
|
226
223
|
// No identity provider yet: each browser is a visitor of its own.
|
|
@@ -249,9 +246,11 @@ COMPOSE_PROJECT_NAME=
|
|
|
249
246
|
DEFAULT_ADMIN_EMAIL=admin@example.com
|
|
250
247
|
DEFAULT_ADMIN_PASSWORD=
|
|
251
248
|
BOOTSTRAP_MANAGEMENT_KEY=
|
|
252
|
-
# Session signing,
|
|
249
|
+
# Session signing, the key the secrets store is encrypted with, and object
|
|
250
|
+
# storage's password (storage is published on this machine for uploads).
|
|
253
251
|
BETTER_AUTH_SECRET=
|
|
254
252
|
SECRETS_MASTER_KEY=
|
|
253
|
+
S3_SECRET_ACCESS_KEY=
|
|
255
254
|
# Any OpenAI-compatible embeddings endpoint and model. Tool search and
|
|
256
255
|
# knowledge collections embed with it, and stay off while the key is blank.
|
|
257
256
|
EMBEDDING_BASE_URL=https://api.openai.com/v1
|
|
@@ -288,17 +287,14 @@ MODEL_PROVIDER_MODEL=
|
|
|
288
287
|
function dockerCompose() {
|
|
289
288
|
return `# Cortex, for this project's agents to run on: the console at
|
|
290
289
|
# http://localhost:{{port.cortex}}, the engine widgets and trigger callers reach at
|
|
291
|
-
# http://localhost:{{port.engine}},
|
|
292
|
-
#
|
|
290
|
+
# http://localhost:{{port.engine}}, storage the console uploads to at
|
|
291
|
+
# http://localhost:{{port.storage}}, and the stores they keep state in.
|
|
292
|
+
# \`bun run dev\` starts it; .env holds its secrets.
|
|
293
293
|
#
|
|
294
294
|
# The host ports were picked at scaffold time from what was free on this
|
|
295
295
|
# machine. Move one and the URLs that name it here, in .env and in README.md
|
|
296
296
|
# follow.
|
|
297
297
|
# CORTEX_VERSION, set in the shell, runs another release of the image.
|
|
298
|
-
#
|
|
299
|
-
# The console's browser uploads (knowledge documents) go straight to
|
|
300
|
-
# S3_ENDPOINT, which only this stack resolves. Point it at storage the
|
|
301
|
-
# browser reaches too before relying on them.
|
|
302
298
|
|
|
303
299
|
# The stack runs as .env's COMPOSE_PROJECT_NAME, drawn for each checkout by
|
|
304
300
|
# setup.ts, so no two checkouts share containers or volumes, whatever their
|
|
@@ -316,9 +312,13 @@ x-cortex: &cortex
|
|
|
316
312
|
DATABASE_URL: postgres://postgres:postgres@postgres:5432/cortex
|
|
317
313
|
REDIS_URL: redis://redis:6379
|
|
318
314
|
S3_ENDPOINT: http://minio:9000
|
|
315
|
+
# The console's uploads go straight to storage, from the browser.
|
|
316
|
+
S3_PUBLIC_ENDPOINT: http://localhost:{{port.storage}}
|
|
319
317
|
S3_REGION: us-east-1
|
|
320
318
|
S3_ACCESS_KEY_ID: minio
|
|
321
|
-
|
|
319
|
+
# Interpolated like storage's own, so both read the one value even when the
|
|
320
|
+
# shell exports another.
|
|
321
|
+
S3_SECRET_ACCESS_KEY: \${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}
|
|
322
322
|
S3_BUCKET: cortex
|
|
323
323
|
BETTER_AUTH_ALLOWED_HOSTS: localhost:{{port.cortex}},127.0.0.1:{{port.cortex}}
|
|
324
324
|
# The dimension the image's migrations give the vector columns.
|
|
@@ -366,9 +366,13 @@ services:
|
|
|
366
366
|
command: server /data
|
|
367
367
|
environment:
|
|
368
368
|
MINIO_ROOT_USER: minio
|
|
369
|
-
MINIO_ROOT_PASSWORD:
|
|
369
|
+
MINIO_ROOT_PASSWORD: \${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}
|
|
370
|
+
# The console's uploads come from its origin alone.
|
|
371
|
+
MINIO_API_CORS_ALLOW_ORIGIN: http://localhost:{{port.cortex}},http://127.0.0.1:{{port.cortex}}
|
|
370
372
|
volumes:
|
|
371
373
|
- minio-data:/data
|
|
374
|
+
ports:
|
|
375
|
+
- "127.0.0.1:{{port.storage}}:9000"
|
|
372
376
|
healthcheck:
|
|
373
377
|
test: ["CMD", "bash", "-c", "</dev/tcp/127.0.0.1/9000"]
|
|
374
378
|
interval: 5s
|
|
@@ -381,7 +385,7 @@ services:
|
|
|
381
385
|
minio:
|
|
382
386
|
condition: service_healthy
|
|
383
387
|
environment:
|
|
384
|
-
MC_HOST_local: http://minio
|
|
388
|
+
MC_HOST_local: http://minio:\${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}@minio:9000
|
|
385
389
|
command: ["mb", "--ignore-existing", "local/cortex"]
|
|
386
390
|
|
|
387
391
|
# One-shot: migrate the database, then create the first admin and its key.
|
|
@@ -454,7 +458,7 @@ function readme({ name, server, datasets, setup }: Project) {
|
|
|
454
458
|
const gettingStarted = setup
|
|
455
459
|
? `That starts Cortex (\`docker compose up\`), this backend, and \`cortex setup\` in one dashboard.
|
|
456
460
|
Setup waits for Cortex, registers this backend as the server \`${server}\`, adds the model provider
|
|
457
|
-
in \`.env\`, and applies the manifest. Then it prints where to chat with \`${
|
|
461
|
+
in \`.env\`, and applies the manifest. Then it prints where to chat with \`${AGENT_NAME}\`, and the first
|
|
458
462
|
time, how to sign in. It adds only what is missing, so every \`bun run dev\` runs it again.
|
|
459
463
|
|
|
460
464
|
While \`MODEL_PROVIDER_*\` in \`.env\` is blank, setup registers the server and applies nothing:
|
|
@@ -481,7 +485,7 @@ A backend for Cortex agents, and Cortex itself to run them on.
|
|
|
481
485
|
? "\n- `datasets.ts` serves an `orders` table as a dataset host, for agents to ask grouped\n questions of."
|
|
482
486
|
: ""
|
|
483
487
|
}
|
|
484
|
-
- \`cortex.manifest.ts\` declares the \`${
|
|
488
|
+
- \`cortex.manifest.ts\` declares the \`${AGENT_NAME}\` agent on this server${
|
|
485
489
|
datasets ? " and the dataset host" : ""
|
|
486
490
|
}. \`cortex apply\` makes Cortex match it.
|
|
487
491
|
- \`docker-compose.yml\` runs Cortex: its app, worker and engine, and the stores they use.
|
|
@@ -501,7 +505,7 @@ bun run dev
|
|
|
501
505
|
|
|
502
506
|
${gettingStarted}
|
|
503
507
|
|
|
504
|
-
Then chat with \`${
|
|
508
|
+
Then chat with \`${AGENT_NAME}\` on the console's **Playground** page. Edit \`cortex.manifest.ts\` and run
|
|
505
509
|
\`bun cortex apply\` to change it; an apply changes only what the edit changed. The CLI reaches this
|
|
506
510
|
checkout's Cortex through \`CORTEX_URL\` and \`CORTEX_KEY\` in \`.env\`, so it needs no login, and
|
|
507
511
|
\`-p <profile>\` picks a saved one instead.
|
|
@@ -532,7 +536,7 @@ pre-commit hook or CI job should run. Each is also available on its own: \`forma
|
|
|
532
536
|
* are drawn by the caller.
|
|
533
537
|
*/
|
|
534
538
|
export function scaffoldFiles(input: Input) {
|
|
535
|
-
const project = { ...input, server: input.name
|
|
539
|
+
const project = { ...input, server: manifestIdOf(input.name) };
|
|
536
540
|
const files = {
|
|
537
541
|
"server.ts": serverTs,
|
|
538
542
|
"setup.ts": setupTs,
|
|
@@ -547,7 +551,7 @@ export function scaffoldFiles(input: Input) {
|
|
|
547
551
|
".gitignore": gitignoreText,
|
|
548
552
|
".dockerignore": dockerignoreText,
|
|
549
553
|
"README.md": readme(project),
|
|
550
|
-
"
|
|
554
|
+
".oxlintrc.json": oxlintrc,
|
|
551
555
|
"prettier.config.mjs": prettierConfig,
|
|
552
556
|
"cspell.json": cspellJson,
|
|
553
557
|
".fallowrc.jsonc": fallowrc,
|
package/src/scaffold/ports.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Host ports for the Cortex stack a generated project runs, chosen once at
|
|
3
3
|
* scaffold time.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* project's callers reach them
|
|
7
|
-
*
|
|
5
|
+
* The app, the engine and storage are published: the browser and this
|
|
6
|
+
* project's callers reach them (storage through the upload links Cortex
|
|
7
|
+
* signs), and nothing outside the stack reaches its databases. The backend
|
|
8
|
+
* listens on the fourth. Fixed ports would collide with
|
|
8
9
|
* the next project scaffolded on the same machine, so each project draws its
|
|
9
10
|
* own. Container ports stay canonical.
|
|
10
11
|
*
|
|
@@ -60,7 +61,7 @@ export function allocatePorts() {
|
|
|
60
61
|
return port;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
return { cortex: next(), engine: next(), server: next() };
|
|
64
|
+
return { cortex: next(), engine: next(), storage: next(), server: next() };
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export type Ports = ReturnType<typeof allocatePorts>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
// typescript-eslint's recommended type-checked set; nothing else runs.
|
|
4
|
+
"categories": {
|
|
5
|
+
"correctness": "off"
|
|
6
|
+
},
|
|
7
|
+
"options": {
|
|
8
|
+
"typeAware": true,
|
|
9
|
+
"reportUnusedDisableDirectives": "warn"
|
|
10
|
+
},
|
|
11
|
+
"ignorePatterns": ["**/node_modules/**", "**/dist/**", "**/.fallow/**"],
|
|
12
|
+
"overrides": [
|
|
13
|
+
{
|
|
14
|
+
"files": ["**/*.ts"],
|
|
15
|
+
"plugins": ["typescript"],
|
|
16
|
+
"rules": {
|
|
17
|
+
"no-var": "error",
|
|
18
|
+
"prefer-const": "error",
|
|
19
|
+
"prefer-rest-params": "error",
|
|
20
|
+
"prefer-spread": "error",
|
|
21
|
+
"no-array-constructor": "error",
|
|
22
|
+
"no-unused-expressions": "error",
|
|
23
|
+
"no-unused-vars": "error",
|
|
24
|
+
"typescript/await-thenable": "error",
|
|
25
|
+
"typescript/ban-ts-comment": "error",
|
|
26
|
+
"typescript/no-array-delete": "error",
|
|
27
|
+
"typescript/no-base-to-string": "error",
|
|
28
|
+
"typescript/no-duplicate-enum-values": "error",
|
|
29
|
+
"typescript/no-duplicate-type-constituents": "error",
|
|
30
|
+
"typescript/no-empty-object-type": "error",
|
|
31
|
+
"typescript/no-explicit-any": "error",
|
|
32
|
+
"typescript/no-extra-non-null-assertion": "error",
|
|
33
|
+
"typescript/no-floating-promises": "error",
|
|
34
|
+
"typescript/no-for-in-array": "error",
|
|
35
|
+
"typescript/no-implied-eval": "error",
|
|
36
|
+
"typescript/no-misused-new": "error",
|
|
37
|
+
"typescript/no-misused-promises": "error",
|
|
38
|
+
"typescript/no-namespace": "error",
|
|
39
|
+
"typescript/no-non-null-asserted-optional-chain": "error",
|
|
40
|
+
"typescript/no-redundant-type-constituents": "error",
|
|
41
|
+
"typescript/no-require-imports": "error",
|
|
42
|
+
"typescript/no-this-alias": "error",
|
|
43
|
+
"typescript/no-unnecessary-type-assertion": "error",
|
|
44
|
+
"typescript/no-unnecessary-type-constraint": "error",
|
|
45
|
+
"typescript/no-unsafe-argument": "error",
|
|
46
|
+
"typescript/no-unsafe-assignment": "error",
|
|
47
|
+
"typescript/no-unsafe-call": "error",
|
|
48
|
+
"typescript/no-unsafe-declaration-merging": "error",
|
|
49
|
+
"typescript/no-unsafe-enum-comparison": "error",
|
|
50
|
+
"typescript/no-unsafe-function-type": "error",
|
|
51
|
+
"typescript/no-unsafe-member-access": "error",
|
|
52
|
+
"typescript/no-unsafe-return": "error",
|
|
53
|
+
"typescript/no-unsafe-unary-minus": "error",
|
|
54
|
+
"typescript/no-wrapper-object-types": "error",
|
|
55
|
+
"typescript/only-throw-error": "error",
|
|
56
|
+
"typescript/prefer-as-const": "error",
|
|
57
|
+
"typescript/prefer-namespace-keyword": "error",
|
|
58
|
+
"typescript/prefer-promise-reject-errors": "error",
|
|
59
|
+
"typescript/require-await": "error",
|
|
60
|
+
"typescript/restrict-plus-operands": "error",
|
|
61
|
+
"typescript/restrict-template-expressions": "error",
|
|
62
|
+
"typescript/triple-slash-reference": "error",
|
|
63
|
+
"typescript/unbound-method": "error"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -30,6 +30,7 @@ const GENERATED = [
|
|
|
30
30
|
["BOOTSTRAP_MANAGEMENT_KEY", () => `cortex_mgmt_${token()}`],
|
|
31
31
|
["BETTER_AUTH_SECRET", key],
|
|
32
32
|
["SECRETS_MASTER_KEY", key],
|
|
33
|
+
["S3_SECRET_ACCESS_KEY", token],
|
|
33
34
|
] as const;
|
|
34
35
|
|
|
35
36
|
const source = existsSync(".env") ? ".env" : ".env.example";
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
-
import prettierConfig from "eslint-config-prettier";
|
|
3
|
-
import prettierPlugin from "eslint-plugin-prettier";
|
|
4
|
-
import tseslint from "typescript-eslint";
|
|
5
|
-
|
|
6
|
-
export default defineConfig([
|
|
7
|
-
globalIgnores(["**/node_modules/**", "**/dist/**", "**/.fallow/**"]),
|
|
8
|
-
prettierConfig,
|
|
9
|
-
{
|
|
10
|
-
plugins: { prettier: prettierPlugin },
|
|
11
|
-
rules: { "prettier/prettier": "error" },
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
files: ["**/*.ts"],
|
|
15
|
-
extends: [tseslint.configs.recommendedTypeChecked],
|
|
16
|
-
languageOptions: {
|
|
17
|
-
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
]);
|