@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.
@@ -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/eslint-config`, `@m6d/prettier-config` and `@m6d/tsconfig`:
6
- * those are `private: true` at `0.0.0` and resolvable only through this
7
- * workspace, and a standalone scaffold has no workspace.
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 eslintConfig from "@/scaffold/templates/eslint.config.mjs.txt";
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 `mprocs`, which its own
23
- // package.json names, is seeded: a stranger's vocabulary is noise in a repo.
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.18.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 slug for its console's own assistant.
41
+ * Not `assistant`: Cortex holds that name for its console's own assistant.
43
42
  */
44
- export const AGENT_SLUG = "helper";
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.3.14",
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: "~6.0.3",
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` takes it: lowercase words and hyphens. */
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: "eslint",
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.6.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
- slug: "${AGENT_SLUG}",
223
- name: "Helper",
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, and the key the secrets store is encrypted with.
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}}, and the stores they keep state in. \`bun run dev\`
292
- # starts it; .env holds its secrets.
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
- S3_SECRET_ACCESS_KEY: minio-secret-key
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: minio-secret-key
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:minio-secret-key@minio:9000
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 \`${AGENT_SLUG}\`, and the first
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 \`${AGENT_SLUG}\` agent on this server${
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 \`${AGENT_SLUG}\` on the console's **Playground** page. Edit \`cortex.manifest.ts\` and run
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.slice(0, NAME_MAX_LENGTH) };
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
- "eslint.config.mjs": eslintConfig,
554
+ ".oxlintrc.json": oxlintrc,
551
555
  "prettier.config.mjs": prettierConfig,
552
556
  "cspell.json": cspellJson,
553
557
  ".fallowrc.jsonc": fallowrc,
@@ -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
- * Only the app and the engine are published: the browser and this
6
- * project's callers reach them, and nothing outside the stack reaches its
7
- * databases. The backend listens on the third. Fixed ports would collide with
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>;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.2",
3
3
  "language": "en",
4
- "words": ["mprocs"],
4
+ "words": ["mprocs", "oxlint", "tsgolint"],
5
5
  "ignorePaths": [
6
6
  "node_modules/**",
7
7
  "out/**",
@@ -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
- ]);