@m6d/cortex-cli 0.18.0 → 0.20.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.
@@ -1476,22 +1476,6 @@ export const agentSettingsSchema = z.strictObject({
1476
1476
  /** Off, end users cannot message the agent: only its triggers reach it. */
1477
1477
  messageTrigger: z.boolean().default(true),
1478
1478
  defaultLocale: localeSchema.default(BASE_LOCALE),
1479
- rateLimits: z
1480
- .strictObject({
1481
- allUsers: z
1482
- .strictObject({
1483
- messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "allUsers"),
1484
- tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "allUsers"),
1485
- })
1486
- .prefault({}),
1487
- perUser: z
1488
- .strictObject({
1489
- messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "perUser"),
1490
- tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "perUser"),
1491
- })
1492
- .prefault({}),
1493
- })
1494
- .prefault({}),
1495
1479
  context: z
1496
1480
  .strictObject({
1497
1481
  maxContextTokens: setting(agentContext.maxContextTokens),
@@ -1516,30 +1500,77 @@ export const agentSettingsSchema = z.strictObject({
1516
1500
  /** An agent's name in characters. The console caps its editor at the same. */
1517
1501
  export const AGENT_LIMITS = { nameLength: 120 } as const;
1518
1502
 
1519
- const manifestAgentSchema = z.strictObject({
1520
- slug: agentSlugSchema,
1521
- name: z.string().trim().min(1).max(AGENT_LIMITS.nameLength),
1522
- /** The server it belongs to, by name; a manifest never creates one. */
1523
- server: nameSchema,
1524
- /** Rich text: it may mention tools and skills, and read `{{variables}}`. */
1525
- prompt: z.string().trim().min(1),
1526
- auth: agentAuthSchema,
1527
- /**
1528
- * `chat` left out runs on the server's pick; `vision` and
1529
- * `summarization` left out use none of their own.
1530
- */
1531
- models: z
1532
- .strictObject({
1533
- chat: modelSchema.optional(),
1534
- vision: modelSchema.omit({ effort: true }).optional(),
1535
- summarization: modelSchema.omit({ effort: true }).optional(),
1536
- })
1537
- .default({}),
1538
- settings: agentSettingsSchema.prefault({}),
1539
- suggestions: suggestionsSchema.default(() => byLocale(() => [])),
1540
- greeting: greetingSchema.default(() => byLocale(() => "")),
1541
- overridable: overridable(["name", "prompt", "models", "settings", "suggestions", "greeting"]),
1542
- });
1503
+ const manifestAgentSchema = z
1504
+ .strictObject({
1505
+ slug: agentSlugSchema,
1506
+ name: z.string().trim().min(1).max(AGENT_LIMITS.nameLength),
1507
+ /**
1508
+ * The server it belongs to, by name; a manifest never creates one. Left
1509
+ * out, the agent has no server: no native tools, no server variables,
1510
+ * no `server` auth, and no model to inherit, so it names its own `chat`.
1511
+ */
1512
+ server: nameSchema.optional(),
1513
+ /** Rich text: it may mention tools and skills, and read `{{variables}}`. */
1514
+ prompt: z.string().trim().min(1),
1515
+ auth: agentAuthSchema,
1516
+ /**
1517
+ * `chat` left out runs on the server's pick; `vision` and
1518
+ * `summarization` left out use none of their own.
1519
+ */
1520
+ models: z
1521
+ .strictObject({
1522
+ chat: modelSchema.optional(),
1523
+ vision: modelSchema.omit({ effort: true }).optional(),
1524
+ summarization: modelSchema.omit({ effort: true }).optional(),
1525
+ })
1526
+ .default({}),
1527
+ settings: agentSettingsSchema.prefault({}),
1528
+ /** Messages a minute and model tokens a UTC day, for all users together and for each; null, no limit. */
1529
+ rateLimits: z
1530
+ .strictObject({
1531
+ allUsers: z
1532
+ .strictObject({
1533
+ messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "allUsers"),
1534
+ tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "allUsers"),
1535
+ })
1536
+ .prefault({}),
1537
+ perUser: z
1538
+ .strictObject({
1539
+ messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "perUser"),
1540
+ tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "perUser"),
1541
+ })
1542
+ .prefault({}),
1543
+ })
1544
+ .prefault({}),
1545
+ suggestions: suggestionsSchema.default(() => byLocale(() => [])),
1546
+ greeting: greetingSchema.default(() => byLocale(() => "")),
1547
+ overridable: overridable([
1548
+ "name",
1549
+ "prompt",
1550
+ "models",
1551
+ "settings",
1552
+ "rateLimits",
1553
+ "suggestions",
1554
+ "greeting",
1555
+ ]),
1556
+ })
1557
+ .superRefine((agent, context) => {
1558
+ if (agent.server !== undefined) return;
1559
+ if (agent.auth.kind === "server") {
1560
+ context.addIssue({
1561
+ code: "custom",
1562
+ path: ["auth"],
1563
+ message: "server auth needs a server",
1564
+ });
1565
+ }
1566
+ if (agent.models.chat === undefined) {
1567
+ context.addIssue({
1568
+ code: "custom",
1569
+ path: ["models", "chat"],
1570
+ message: "an agent with no server names its own chat model",
1571
+ });
1572
+ }
1573
+ });
1543
1574
  export type ManifestAgent = z.input<typeof manifestAgentSchema>;
1544
1575
 
1545
1576
  /**
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
2
2
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
 
5
- import { isRecord } from "@cortex/contracts/json";
5
+ import { createManagementClient, MANAGEMENT_BASE_PATH } from "@cortex/contracts/management";
6
+ import type { ManagementTarget } from "@cortex/contracts/management";
6
7
  import { z } from "zod";
7
8
 
8
9
  import { profilesPath } from "./profiles";
@@ -109,7 +110,7 @@ export async function loadOpenApi(url: string) {
109
110
  const path = cachePath(url);
110
111
  const cached = readCache(path);
111
112
  try {
112
- const response = await fetch(`${url}/api/management/openapi.json`, {
113
+ const response = await fetch(`${url}${MANAGEMENT_BASE_PATH}/openapi.json`, {
113
114
  headers: cached ? { "If-None-Match": cached.etag } : {},
114
115
  });
115
116
  if (response.status === 304 && cached)
@@ -159,71 +160,30 @@ export function inputSchemaOf(operation: OpenApiOperation) {
159
160
  }
160
161
 
161
162
  /**
162
- * One management call. A query's input object is spread over the query
163
- * string, one parameter per field, with a scalar written plainly and
164
- * anything else JSON-encoded, which is how the document declares it. A
165
- * mutation posts its input as the JSON body. A refusal throws
166
- * `ManagementError` with the error body's code, message, detail and sentence.
163
+ * Where `profile`'s management calls go; a Cortex that does not answer at
164
+ * all rejects as `UnreachableError`, apart from one that refused.
167
165
  */
168
- export async function callManagement(
169
- profile: { url: string; key: string },
170
- method: "get" | "post",
171
- path: string,
172
- input: unknown,
173
- ) {
174
- const base = `${profile.url}/api/management${path}`;
175
- const url = method === "get" && isRecord(input) ? `${base}${queryString(input)}` : base;
176
- const response = await fetch(url, {
177
- method: method.toUpperCase(),
178
- headers: {
179
- Authorization: `Bearer ${profile.key}`,
180
- ...(method === "post" ? { "Content-Type": "application/json" } : {}),
181
- },
182
- body: method === "post" && input !== undefined ? JSON.stringify(input) : undefined,
183
- }).catch((error: unknown) => {
184
- throw new UnreachableError(error);
185
- });
186
- const body: unknown = await response.json().catch(() => null);
187
- if (!response.ok) {
188
- const { code, message, refusal, cause } = (body ?? {}) as {
189
- code?: string;
190
- message?: string;
191
- refusal?: string;
192
- cause?: unknown;
193
- };
194
- throw new ManagementError(
195
- response.status,
196
- code ?? "error",
197
- message ?? response.statusText,
198
- isRecord(cause) ? cause : undefined,
199
- refusal,
200
- );
201
- }
202
- return body;
203
- }
204
-
205
- function isScalar(value: unknown): value is string | number | boolean | null {
206
- return value === null || ["string", "number", "boolean"].includes(typeof value);
166
+ export function managementTarget(profile: { url: string; key: string }) {
167
+ return {
168
+ ...profile,
169
+ // Every day range a command takes is this machine's days.
170
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
171
+ fetch: (url: string, init: RequestInit) =>
172
+ fetch(url, init).catch((error: unknown) => {
173
+ throw new UnreachableError(error);
174
+ }),
175
+ } satisfies ManagementTarget;
207
176
  }
208
177
 
209
- /** A scalar as itself, an array of scalars as a repeated parameter, anything else as JSON, matching what the document declares. */
210
- function queryString(input: Record<string, unknown>) {
211
- const params = new URLSearchParams();
212
- for (const [name, value] of Object.entries(input)) {
213
- if (value === undefined) continue;
214
- if (isScalar(value)) params.set(name, String(value));
215
- else if (Array.isArray(value) && value.length > 0 && value.every(isScalar)) {
216
- for (const item of value) params.append(name, String(item));
217
- } else params.set(name, JSON.stringify(value));
218
- }
219
- const encoded = params.toString();
220
- return encoded === "" ? "" : `?${encoded}`;
178
+ /** The typed client for the calls the CLI makes by name, over `managementTarget`. */
179
+ export function managementClient(profile: { url: string; key: string }) {
180
+ return createManagementClient(managementTarget(profile));
221
181
  }
222
182
 
223
183
  /**
224
184
  * Cortex did not answer at all: refused, reset, unresolvable. `fetch` rejects
225
185
  * only for these, with whatever its runtime throws (Bun's is a plain `Error`
226
- * with a `code`), so `callManagement` names them once here.
186
+ * with a `code`), so `managementTarget` names them once here.
227
187
  */
228
188
  export class UnreachableError extends Error {
229
189
  public constructor(cause: unknown) {
@@ -231,21 +191,3 @@ export class UnreachableError extends Error {
231
191
  this.name = "UnreachableError";
232
192
  }
233
193
  }
234
-
235
- /**
236
- * A refusal; `detail` is what the refusal names, when it names something (an
237
- * entry, the row holding a name), and `refusal` the sentence Cortex words it
238
- * in, when it has one.
239
- */
240
- export class ManagementError extends Error {
241
- public constructor(
242
- public readonly status: number,
243
- public readonly code: string,
244
- message: string,
245
- public readonly detail?: Record<string, unknown>,
246
- public readonly refusal?: string,
247
- ) {
248
- super(message);
249
- this.name = "ManagementError";
250
- }
251
- }
@@ -2,9 +2,10 @@
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
11
  import { NAME_MAX_LENGTH } from "@cortex/contracts/manifest";
@@ -16,11 +17,11 @@ import datasetsTs from "@/scaffold/templates/datasets.ts.txt";
16
17
  import tsconfigJson from "@/scaffold/templates/tsconfig.json.txt";
17
18
  import gitignoreText from "@/scaffold/templates/gitignore.txt";
18
19
  import dockerignoreText from "@/scaffold/templates/dockerignore.txt";
19
- import eslintConfig from "@/scaffold/templates/eslint.config.mjs.txt";
20
+ import oxlintrc from "@/scaffold/templates/oxlintrc.json.txt";
20
21
  import prettierConfig from "@/scaffold/templates/prettier.config.mjs.txt";
21
22
  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.
23
+ // Project jargon is the project's to add. Only the tool names its own
24
+ // package.json carries are seeded: a stranger's vocabulary is noise in a repo.
24
25
  import cspellJson from "@/scaffold/templates/cspell.json.txt";
25
26
  import { applyPorts, type Ports } from "@/scaffold/ports";
26
27
 
@@ -29,7 +30,7 @@ import { applyPorts, type Ports } from "@/scaffold/ports";
29
30
  * `@m6d/cortex` range below are stamped by scripts/sync-package-versions.ts
30
31
  * before the CLI publishes, so a scaffold pins what was current then.
31
32
  */
32
- const CORTEX_VERSION = "0.17.0";
33
+ const CORTEX_VERSION = "0.19.0";
33
34
 
34
35
  /** This machine as Cortex's containers name it: the one private host the stack's egress policy admits. */
35
36
  export const DOCKER_HOST = "host.docker.internal";
@@ -54,18 +55,16 @@ export const EMBEDDING = { model: "text-embedding-3-small", dimension: 768 } as
54
55
  const DEV_DEPENDENCIES = {
55
56
  // `cortex apply`, `cortex setup` and the management commands, at the release that wrote the project.
56
57
  "@m6d/cortex-cli": `~${pkg.version}`,
57
- "@types/bun": "1.3.14",
58
+ "@types/bun": "1.4.4",
58
59
  // The two runners of `bun run dev` and `bun run dev:agent`.
59
60
  concurrently: "^10.0.6",
60
61
  cspell: "^9.6.4",
61
- eslint: "^9",
62
- "eslint-config-prettier": "^10.1.8",
63
- "eslint-plugin-prettier": "^5.5.5",
64
62
  fallow: "^3.9.1",
65
63
  mprocs: "^0.9.6",
64
+ oxlint: "^1.90.0",
65
+ "oxlint-tsgolint": "^7.0.2005",
66
66
  prettier: "^3.8.1",
67
- typescript: "~6.0.3",
68
- "typescript-eslint": "^8.65.0",
67
+ typescript: "^7.0.2",
69
68
  };
70
69
 
71
70
  /**
@@ -145,12 +144,12 @@ function packageJson({ name, datasets, setup }: Project) {
145
144
  postinstall: "bun run --if-present setup.ts",
146
145
  format: "prettier --write .",
147
146
  "format:check": "prettier --check .",
148
- lint: "eslint",
147
+ lint: "oxlint",
149
148
  spellcheck: 'cspell "**/*.{ts,mjs,json,md}"',
150
149
  verify: "bun run format:check && bun run lint && bun run spellcheck && bun run check && bun run audit",
151
150
  },
152
151
  dependencies: {
153
- "@m6d/cortex": "~0.5.0",
152
+ "@m6d/cortex": "~0.7.0",
154
153
  // drizzle 1.0 is in beta; this is the build `@m6d/cortex/drizzle` is tested against.
155
154
  ...(datasets ? { "drizzle-orm": "1.0.0-beta.15-859cf75" } : {}),
156
155
  hono: "^4.12.3",
@@ -249,9 +248,11 @@ COMPOSE_PROJECT_NAME=
249
248
  DEFAULT_ADMIN_EMAIL=admin@example.com
250
249
  DEFAULT_ADMIN_PASSWORD=
251
250
  BOOTSTRAP_MANAGEMENT_KEY=
252
- # Session signing, and the key the secrets store is encrypted with.
251
+ # Session signing, the key the secrets store is encrypted with, and object
252
+ # storage's password (storage is published on this machine for uploads).
253
253
  BETTER_AUTH_SECRET=
254
254
  SECRETS_MASTER_KEY=
255
+ S3_SECRET_ACCESS_KEY=
255
256
  # Any OpenAI-compatible embeddings endpoint and model. Tool search and
256
257
  # knowledge collections embed with it, and stay off while the key is blank.
257
258
  EMBEDDING_BASE_URL=https://api.openai.com/v1
@@ -288,17 +289,14 @@ MODEL_PROVIDER_MODEL=
288
289
  function dockerCompose() {
289
290
  return `# Cortex, for this project's agents to run on: the console at
290
291
  # 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.
292
+ # http://localhost:{{port.engine}}, storage the console uploads to at
293
+ # http://localhost:{{port.storage}}, and the stores they keep state in.
294
+ # \`bun run dev\` starts it; .env holds its secrets.
293
295
  #
294
296
  # The host ports were picked at scaffold time from what was free on this
295
297
  # machine. Move one and the URLs that name it here, in .env and in README.md
296
298
  # follow.
297
299
  # 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
300
 
303
301
  # The stack runs as .env's COMPOSE_PROJECT_NAME, drawn for each checkout by
304
302
  # setup.ts, so no two checkouts share containers or volumes, whatever their
@@ -316,9 +314,13 @@ x-cortex: &cortex
316
314
  DATABASE_URL: postgres://postgres:postgres@postgres:5432/cortex
317
315
  REDIS_URL: redis://redis:6379
318
316
  S3_ENDPOINT: http://minio:9000
317
+ # The console's uploads go straight to storage, from the browser.
318
+ S3_PUBLIC_ENDPOINT: http://localhost:{{port.storage}}
319
319
  S3_REGION: us-east-1
320
320
  S3_ACCESS_KEY_ID: minio
321
- S3_SECRET_ACCESS_KEY: minio-secret-key
321
+ # Interpolated like storage's own, so both read the one value even when the
322
+ # shell exports another.
323
+ S3_SECRET_ACCESS_KEY: \${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}
322
324
  S3_BUCKET: cortex
323
325
  BETTER_AUTH_ALLOWED_HOSTS: localhost:{{port.cortex}},127.0.0.1:{{port.cortex}}
324
326
  # The dimension the image's migrations give the vector columns.
@@ -366,9 +368,13 @@ services:
366
368
  command: server /data
367
369
  environment:
368
370
  MINIO_ROOT_USER: minio
369
- MINIO_ROOT_PASSWORD: minio-secret-key
371
+ MINIO_ROOT_PASSWORD: \${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}
372
+ # The console's uploads come from its origin alone.
373
+ MINIO_API_CORS_ALLOW_ORIGIN: http://localhost:{{port.cortex}},http://127.0.0.1:{{port.cortex}}
370
374
  volumes:
371
375
  - minio-data:/data
376
+ ports:
377
+ - "127.0.0.1:{{port.storage}}:9000"
372
378
  healthcheck:
373
379
  test: ["CMD", "bash", "-c", "</dev/tcp/127.0.0.1/9000"]
374
380
  interval: 5s
@@ -381,7 +387,7 @@ services:
381
387
  minio:
382
388
  condition: service_healthy
383
389
  environment:
384
- MC_HOST_local: http://minio:minio-secret-key@minio:9000
390
+ MC_HOST_local: http://minio:\${S3_SECRET_ACCESS_KEY:?run bun install to generate .env}@minio:9000
385
391
  command: ["mb", "--ignore-existing", "local/cortex"]
386
392
 
387
393
  # One-shot: migrate the database, then create the first admin and its key.
@@ -547,7 +553,7 @@ export function scaffoldFiles(input: Input) {
547
553
  ".gitignore": gitignoreText,
548
554
  ".dockerignore": dockerignoreText,
549
555
  "README.md": readme(project),
550
- "eslint.config.mjs": eslintConfig,
556
+ ".oxlintrc.json": oxlintrc,
551
557
  "prettier.config.mjs": prettierConfig,
552
558
  "cspell.json": cspellJson,
553
559
  ".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
- ]);