@m6d/cortex-cli 0.19.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.
- package/README.md +1 -1
- package/package.json +3 -3
- package/src/contracts/datasets/dataset-sql.ts +1 -1
- package/src/contracts/management/index.ts +13 -1
- package/src/contracts/management/procedures.generated.ts +73 -71
- package/src/contracts/manifest/index.ts +71 -49
- package/src/management/client.ts +2 -0
- package/src/scaffold/files.ts +32 -26
- 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/README.md
CHANGED
|
@@ -38,7 +38,7 @@ Scaffolds `./<name>`: a backend for Cortex agents, and Cortex itself to run them
|
|
|
38
38
|
- `bun run dev` starts Cortex (`docker compose up`), the backend and `bun cortex setup --wait --yes` in
|
|
39
39
|
an mprocs dashboard. `bun run dev:agent` runs the same through `concurrently`, one labelled log for
|
|
40
40
|
coding agents and CI.
|
|
41
|
-
-
|
|
41
|
+
- oxlint, prettier, cspell, fallow and tsconfig are inlined, so `bun run verify` works standalone.
|
|
42
42
|
|
|
43
43
|
It then runs `bun install`, and `git init` unless the target is already inside a work tree. An
|
|
44
44
|
existing non-empty directory is an error. So is a name like `cortex` or `cortex.`, since the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m6d/cortex-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Scaffold Cortex projects and manage a Cortex deployment",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@cortex/contracts": "workspace:*",
|
|
32
|
-
"@types/bun": "1.
|
|
33
|
-
"typescript": "
|
|
32
|
+
"@types/bun": "1.4.4",
|
|
33
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
|
@@ -1030,7 +1030,7 @@ class Parser {
|
|
|
1030
1030
|
}
|
|
1031
1031
|
|
|
1032
1032
|
/** `a + b * (c - 1)`: `*` and `/` bind before `+` and `-`; a date plus or minus an INTERVAL folds to the date it names. */
|
|
1033
|
-
//
|
|
1033
|
+
// oxlint-disable-next-line m6d/no-explicit-return-type -- mutually recursive with factor and unit
|
|
1034
1034
|
private term(message: string): Term {
|
|
1035
1035
|
let left = this.factor(message);
|
|
1036
1036
|
while (this.peekPunct("+") || this.peekPunct("-")) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import { isRecord } from "../json/index";
|
|
4
|
+
import { TIMEZONE_HEADER } from "../wire/index";
|
|
4
5
|
import { managementProcedureKinds } from "./procedures.generated";
|
|
5
6
|
import type { ManagementProcedures } from "./procedures.generated";
|
|
6
7
|
|
|
@@ -75,10 +76,19 @@ export class ManagementClientError extends Error {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Where management calls go: a console's origin and a management key
|
|
81
|
+
* (`cortex_mgmt_…`); a test hands in its own `fetch`. `locale` (`ar`) and
|
|
82
|
+
* `timeZone` (an IANA zone, `Asia/Dubai`) are the caller's, sent as
|
|
83
|
+
* `Accept-Language` and `X-Cortex-Timezone`: Cortex words its refusals in
|
|
84
|
+
* the one and reads a day range in the other, else English and UTC days. A
|
|
85
|
+
* host acting for many users builds a client per request.
|
|
86
|
+
*/
|
|
79
87
|
export type ManagementTarget = {
|
|
80
88
|
url: string;
|
|
81
89
|
key: string;
|
|
90
|
+
locale?: string;
|
|
91
|
+
timeZone?: string;
|
|
82
92
|
fetch?: (url: string, init: RequestInit) => Promise<Response>;
|
|
83
93
|
};
|
|
84
94
|
|
|
@@ -100,6 +110,8 @@ export async function callManagement(
|
|
|
100
110
|
headers: {
|
|
101
111
|
Authorization: `Bearer ${target.key}`,
|
|
102
112
|
...(method === "post" ? { "Content-Type": "application/json" } : {}),
|
|
113
|
+
...(target.locale ? { "Accept-Language": target.locale } : {}),
|
|
114
|
+
...(target.timeZone ? { [TIMEZONE_HEADER]: target.timeZone } : {}),
|
|
103
115
|
},
|
|
104
116
|
body: method === "post" && input !== undefined ? JSON.stringify(input) : undefined,
|
|
105
117
|
signal,
|
|
@@ -206,10 +206,13 @@ export type StoredSchemaFieldRow = {
|
|
|
206
206
|
|
|
207
207
|
/** Whether each procedure is a query (GET) or a mutation (POST). */
|
|
208
208
|
export const managementProcedureKinds = {
|
|
209
|
+
"agents.addAlias": "mutation",
|
|
209
210
|
"agents.create": "mutation",
|
|
210
211
|
"agents.delete": "mutation",
|
|
212
|
+
"agents.deleteAlias": "mutation",
|
|
211
213
|
"agents.get": "query",
|
|
212
214
|
"agents.list": "query",
|
|
215
|
+
"agents.listAliases": "query",
|
|
213
216
|
"agents.options": "query",
|
|
214
217
|
"agents.publishPrompt": "mutation",
|
|
215
218
|
"agents.rollbackPrompt": "mutation",
|
|
@@ -298,12 +301,9 @@ export const managementProcedureKinds = {
|
|
|
298
301
|
"secrets.set": "mutation",
|
|
299
302
|
"servers.create": "mutation",
|
|
300
303
|
"servers.delete": "mutation",
|
|
301
|
-
"servers.deleteAlias": "mutation",
|
|
302
304
|
"servers.get": "query",
|
|
303
305
|
"servers.list": "query",
|
|
304
|
-
"servers.listAliases": "query",
|
|
305
306
|
"servers.options": "query",
|
|
306
|
-
"servers.setAlias": "mutation",
|
|
307
307
|
"servers.sync": "mutation",
|
|
308
308
|
"servers.update": "mutation",
|
|
309
309
|
"services.create": "mutation",
|
|
@@ -379,12 +379,25 @@ export const managementProcedureKinds = {
|
|
|
379
379
|
|
|
380
380
|
/** Each procedure's input, as it is sent, and output, as JSON carries it. */
|
|
381
381
|
export type ManagementProcedures = {
|
|
382
|
-
/**
|
|
382
|
+
/** Give an agent an alias: a second slug it answers to wherever a slug is resolved. Refused (slug_taken) when an agent's slug or another alias holds the name; to move an alias, delete it and add it to the other agent. */
|
|
383
|
+
"agents.addAlias": {
|
|
384
|
+
input: {
|
|
385
|
+
alias: string;
|
|
386
|
+
agentId: string;
|
|
387
|
+
};
|
|
388
|
+
output: {
|
|
389
|
+
alias: string;
|
|
390
|
+
agentId: string;
|
|
391
|
+
createdAt: string;
|
|
392
|
+
updatedAt: string;
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
/** Create an agent on a registered server, or on none (`serverId` null), fixed for good either way. An agent with no server has no native tools, no server variables and no server auth, and publishes only on a chat model of its own. The draft has no prompt yet; save one with agents.savePrompt before publishing. */
|
|
383
396
|
"agents.create": {
|
|
384
397
|
input: {
|
|
385
398
|
slug: string;
|
|
386
399
|
name: string;
|
|
387
|
-
serverId: string;
|
|
400
|
+
serverId: string | null;
|
|
388
401
|
};
|
|
389
402
|
output: {
|
|
390
403
|
id: string;
|
|
@@ -393,7 +406,7 @@ export type ManagementProcedures = {
|
|
|
393
406
|
overridable: Array<string>;
|
|
394
407
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
395
408
|
slug: string;
|
|
396
|
-
serverId: string;
|
|
409
|
+
serverId: string | null;
|
|
397
410
|
name: string;
|
|
398
411
|
promptDraft: string;
|
|
399
412
|
ragEnabled: boolean;
|
|
@@ -438,7 +451,7 @@ export type ManagementProcedures = {
|
|
|
438
451
|
overridable: Array<string>;
|
|
439
452
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
440
453
|
slug: string;
|
|
441
|
-
serverId: string;
|
|
454
|
+
serverId: string | null;
|
|
442
455
|
name: string;
|
|
443
456
|
promptDraft: string;
|
|
444
457
|
ragEnabled: boolean;
|
|
@@ -470,6 +483,18 @@ export type ManagementProcedures = {
|
|
|
470
483
|
updatedAt: string;
|
|
471
484
|
};
|
|
472
485
|
};
|
|
486
|
+
/** Remove an alias. */
|
|
487
|
+
"agents.deleteAlias": {
|
|
488
|
+
input: {
|
|
489
|
+
alias: string;
|
|
490
|
+
};
|
|
491
|
+
output: {
|
|
492
|
+
alias: string;
|
|
493
|
+
agentId: string;
|
|
494
|
+
createdAt: string;
|
|
495
|
+
updatedAt: string;
|
|
496
|
+
};
|
|
497
|
+
};
|
|
473
498
|
/** One agent by id, including its draft prompt and settings. */
|
|
474
499
|
"agents.get": {
|
|
475
500
|
input: {
|
|
@@ -482,7 +507,7 @@ export type ManagementProcedures = {
|
|
|
482
507
|
overridable: Array<string>;
|
|
483
508
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
484
509
|
slug: string;
|
|
485
|
-
serverId: string;
|
|
510
|
+
serverId: string | null;
|
|
486
511
|
name: string;
|
|
487
512
|
promptDraft: string;
|
|
488
513
|
ragEnabled: boolean;
|
|
@@ -510,7 +535,7 @@ export type ManagementProcedures = {
|
|
|
510
535
|
summarizationModel: string | null;
|
|
511
536
|
context: string | number | boolean | null | Record<string, unknown> | Array<unknown>;
|
|
512
537
|
limits: string | number | boolean | null | Record<string, unknown> | Array<unknown>;
|
|
513
|
-
serverName: string;
|
|
538
|
+
serverName: string | null;
|
|
514
539
|
serverVerifies: boolean;
|
|
515
540
|
variables: Array<{
|
|
516
541
|
name: string;
|
|
@@ -547,7 +572,7 @@ export type ManagementProcedures = {
|
|
|
547
572
|
source: "direct" | "manifest" | "server";
|
|
548
573
|
sourceId: string | null;
|
|
549
574
|
overridable: Array<string>;
|
|
550
|
-
serverName: string;
|
|
575
|
+
serverName: string | null;
|
|
551
576
|
published: boolean;
|
|
552
577
|
toolCount: number;
|
|
553
578
|
serviceCount: number;
|
|
@@ -560,6 +585,27 @@ export type ManagementProcedures = {
|
|
|
560
585
|
filteredCount: number;
|
|
561
586
|
};
|
|
562
587
|
};
|
|
588
|
+
/** List an agent's aliases (second slugs, unique across the deployment beside every agent's own), with pagination. */
|
|
589
|
+
"agents.listAliases": {
|
|
590
|
+
input: {
|
|
591
|
+
pageIndex?: number;
|
|
592
|
+
pageSize?: number;
|
|
593
|
+
q?: string;
|
|
594
|
+
agentId: string;
|
|
595
|
+
};
|
|
596
|
+
output: {
|
|
597
|
+
items: Array<{
|
|
598
|
+
alias: string;
|
|
599
|
+
agentId: string;
|
|
600
|
+
createdAt: string;
|
|
601
|
+
updatedAt: string;
|
|
602
|
+
}>;
|
|
603
|
+
/** Every row the caller may see, ignoring the search and filters. */
|
|
604
|
+
count: number;
|
|
605
|
+
/** The rows matching the search and filters, over all pages: the total to report. */
|
|
606
|
+
filteredCount: number;
|
|
607
|
+
};
|
|
608
|
+
};
|
|
563
609
|
/** Agent id, slug, and name pairs for selectors. */
|
|
564
610
|
"agents.options": {
|
|
565
611
|
input: undefined;
|
|
@@ -567,7 +613,7 @@ export type ManagementProcedures = {
|
|
|
567
613
|
id: string;
|
|
568
614
|
slug: string;
|
|
569
615
|
name: string;
|
|
570
|
-
serverId: string;
|
|
616
|
+
serverId: string | null;
|
|
571
617
|
}>;
|
|
572
618
|
};
|
|
573
619
|
/** Publish the agent's current draft prompt and settings so Cortex serves them. Refused as `auth_required` while the agent's end-user authentication is unchosen, and as `chat_model_required` when neither the agent nor its server picks a chat model. `warnings` says what the publish went ahead despite: published tools the prompt names without an @-mention (neither pinned nor mentioned by a skill it mentions), which the agent usually cannot see. */
|
|
@@ -621,7 +667,7 @@ export type ManagementProcedures = {
|
|
|
621
667
|
overridable: Array<string>;
|
|
622
668
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
623
669
|
slug: string;
|
|
624
|
-
serverId: string;
|
|
670
|
+
serverId: string | null;
|
|
625
671
|
name: string;
|
|
626
672
|
promptDraft: string;
|
|
627
673
|
ragEnabled: boolean;
|
|
@@ -731,7 +777,7 @@ export type ManagementProcedures = {
|
|
|
731
777
|
overridable: Array<string>;
|
|
732
778
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
733
779
|
slug: string;
|
|
734
|
-
serverId: string;
|
|
780
|
+
serverId: string | null;
|
|
735
781
|
name: string;
|
|
736
782
|
promptDraft: string;
|
|
737
783
|
ragEnabled: boolean;
|
|
@@ -776,7 +822,7 @@ export type ManagementProcedures = {
|
|
|
776
822
|
overridable: Array<string>;
|
|
777
823
|
applied: string | number | boolean | null | Record<string, unknown> | Array<unknown> | null;
|
|
778
824
|
slug: string;
|
|
779
|
-
serverId: string;
|
|
825
|
+
serverId: string | null;
|
|
780
826
|
name: string;
|
|
781
827
|
promptDraft: string;
|
|
782
828
|
ragEnabled: boolean;
|
|
@@ -1633,7 +1679,7 @@ export type ManagementProcedures = {
|
|
|
1633
1679
|
agents?: Array<{
|
|
1634
1680
|
slug: string;
|
|
1635
1681
|
name: string;
|
|
1636
|
-
server
|
|
1682
|
+
server?: string;
|
|
1637
1683
|
prompt: string;
|
|
1638
1684
|
auth: {
|
|
1639
1685
|
kind: "jwks";
|
|
@@ -2158,7 +2204,7 @@ export type ManagementProcedures = {
|
|
|
2158
2204
|
agents?: Array<{
|
|
2159
2205
|
slug: string;
|
|
2160
2206
|
name: string;
|
|
2161
|
-
server
|
|
2207
|
+
server?: string;
|
|
2162
2208
|
prompt: string;
|
|
2163
2209
|
auth: {
|
|
2164
2210
|
kind: "jwks";
|
|
@@ -3117,7 +3163,7 @@ export type ManagementProcedures = {
|
|
|
3117
3163
|
outcome: "started" | "awaiting_approval" | "completed" | "failed" | "rejected" | "dropped";
|
|
3118
3164
|
reason: "busy" | "approval_pending" | "budget" | "rate_limited" | "invalid_payload" | "missing_thread_key" | "no_turn" | "no_output" | "invalid_output" | null;
|
|
3119
3165
|
output: unknown;
|
|
3120
|
-
serverName: string;
|
|
3166
|
+
serverName: string | null;
|
|
3121
3167
|
agentSlug: string;
|
|
3122
3168
|
agentName: string;
|
|
3123
3169
|
threadTitle: string | null;
|
|
@@ -3153,8 +3199,8 @@ export type ManagementProcedures = {
|
|
|
3153
3199
|
activeUsers: number;
|
|
3154
3200
|
};
|
|
3155
3201
|
perUser: Array<{
|
|
3156
|
-
|
|
3157
|
-
|
|
3202
|
+
scopeId: string;
|
|
3203
|
+
scopeName: string;
|
|
3158
3204
|
userId: string;
|
|
3159
3205
|
triggerName: string | null;
|
|
3160
3206
|
threads: number;
|
|
@@ -3166,8 +3212,8 @@ export type ManagementProcedures = {
|
|
|
3166
3212
|
messages: number;
|
|
3167
3213
|
}>;
|
|
3168
3214
|
perServer: Array<{
|
|
3169
|
-
serverId: string;
|
|
3170
|
-
serverName: string;
|
|
3215
|
+
serverId: string | null;
|
|
3216
|
+
serverName: string | null;
|
|
3171
3217
|
totalTokens: number;
|
|
3172
3218
|
}>;
|
|
3173
3219
|
perAgent: Array<{
|
|
@@ -3205,7 +3251,7 @@ export type ManagementProcedures = {
|
|
|
3205
3251
|
detail: string | null;
|
|
3206
3252
|
threadId: string;
|
|
3207
3253
|
createdAt: string;
|
|
3208
|
-
serverName: string;
|
|
3254
|
+
serverName: string | null;
|
|
3209
3255
|
agentSlug: string;
|
|
3210
3256
|
agentName: string;
|
|
3211
3257
|
threadTitle: string | null;
|
|
@@ -3387,7 +3433,7 @@ export type ManagementProcedures = {
|
|
|
3387
3433
|
outcome: "started" | "awaiting_approval" | "completed" | "failed" | "rejected" | "dropped";
|
|
3388
3434
|
reason: "busy" | "approval_pending" | "budget" | "rate_limited" | "invalid_payload" | "missing_thread_key" | "no_turn" | "no_output" | "invalid_output" | null;
|
|
3389
3435
|
output: unknown;
|
|
3390
|
-
serverName: string;
|
|
3436
|
+
serverName: string | null;
|
|
3391
3437
|
agentSlug: string;
|
|
3392
3438
|
agentName: string;
|
|
3393
3439
|
threadTitle: string | null;
|
|
@@ -3702,18 +3748,6 @@ export type ManagementProcedures = {
|
|
|
3702
3748
|
updatedAt: string;
|
|
3703
3749
|
};
|
|
3704
3750
|
};
|
|
3705
|
-
/** Remove an alias. */
|
|
3706
|
-
"servers.deleteAlias": {
|
|
3707
|
-
input: {
|
|
3708
|
-
alias: string;
|
|
3709
|
-
};
|
|
3710
|
-
output: {
|
|
3711
|
-
alias: string;
|
|
3712
|
-
agentId: string;
|
|
3713
|
-
createdAt: string;
|
|
3714
|
-
updatedAt: string;
|
|
3715
|
-
};
|
|
3716
|
-
};
|
|
3717
3751
|
/** One server by id with its declared variables and the components whose audience is the server. */
|
|
3718
3752
|
"servers.get": {
|
|
3719
3753
|
input: {
|
|
@@ -3776,27 +3810,6 @@ export type ManagementProcedures = {
|
|
|
3776
3810
|
filteredCount: number;
|
|
3777
3811
|
};
|
|
3778
3812
|
};
|
|
3779
|
-
/** List the aliases (second slugs, unique across the deployment beside every agent's own) of a server's agents, with pagination. */
|
|
3780
|
-
"servers.listAliases": {
|
|
3781
|
-
input: {
|
|
3782
|
-
pageIndex?: number;
|
|
3783
|
-
pageSize?: number;
|
|
3784
|
-
q?: string;
|
|
3785
|
-
serverId: string;
|
|
3786
|
-
};
|
|
3787
|
-
output: {
|
|
3788
|
-
items: Array<{
|
|
3789
|
-
alias: string;
|
|
3790
|
-
agentId: string;
|
|
3791
|
-
agentName: string;
|
|
3792
|
-
agentSlug: string;
|
|
3793
|
-
}>;
|
|
3794
|
-
/** Every row the caller may see, ignoring the search and filters. */
|
|
3795
|
-
count: number;
|
|
3796
|
-
/** The rows matching the search and filters, over all pages: the total to report. */
|
|
3797
|
-
filteredCount: number;
|
|
3798
|
-
};
|
|
3799
|
-
};
|
|
3800
3813
|
/** Server id and name pairs for selectors. Cortex's own system server is left out unless `includeSystem` asks for it, as a filter over its threads does. */
|
|
3801
3814
|
"servers.options": {
|
|
3802
3815
|
input: {
|
|
@@ -3807,17 +3820,6 @@ export type ManagementProcedures = {
|
|
|
3807
3820
|
name: string;
|
|
3808
3821
|
}>;
|
|
3809
3822
|
};
|
|
3810
|
-
/** Point an alias at an agent, creating it or moving it among the agents of one server. Refused (slug_taken) when an agent goes by the name or it is an alias of another server's agent. */
|
|
3811
|
-
"servers.setAlias": {
|
|
3812
|
-
input: {
|
|
3813
|
-
alias: string;
|
|
3814
|
-
agentId: string;
|
|
3815
|
-
};
|
|
3816
|
-
output: {
|
|
3817
|
-
alias: string;
|
|
3818
|
-
agentId: string;
|
|
3819
|
-
};
|
|
3820
|
-
};
|
|
3821
3823
|
/** Read the server's manifest now and apply its variables and native tools; a refused manifest leaves the previous one in place. */
|
|
3822
3824
|
"servers.sync": {
|
|
3823
3825
|
input: {
|
|
@@ -4361,8 +4363,8 @@ export type ManagementProcedures = {
|
|
|
4361
4363
|
triggerName: string | null;
|
|
4362
4364
|
createdAt: string;
|
|
4363
4365
|
updatedAt: string;
|
|
4364
|
-
serverId: string;
|
|
4365
|
-
serverName: string;
|
|
4366
|
+
serverId: string | null;
|
|
4367
|
+
serverName: string | null;
|
|
4366
4368
|
agentSlug: string;
|
|
4367
4369
|
agentId: string;
|
|
4368
4370
|
agentName: string;
|
|
@@ -4400,8 +4402,8 @@ export type ManagementProcedures = {
|
|
|
4400
4402
|
triggerName: string | null;
|
|
4401
4403
|
createdAt: string;
|
|
4402
4404
|
updatedAt: string;
|
|
4403
|
-
serverId: string;
|
|
4404
|
-
serverName: string;
|
|
4405
|
+
serverId: string | null;
|
|
4406
|
+
serverName: string | null;
|
|
4405
4407
|
agentSlug: string;
|
|
4406
4408
|
}>;
|
|
4407
4409
|
/** Every row the caller may see, ignoring the search and filters. */
|
|
@@ -1500,55 +1500,77 @@ export const agentSettingsSchema = z.strictObject({
|
|
|
1500
1500
|
/** An agent's name in characters. The console caps its editor at the same. */
|
|
1501
1501
|
export const AGENT_LIMITS = { nameLength: 120 } as const;
|
|
1502
1502
|
|
|
1503
|
-
const manifestAgentSchema = z
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
"
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
+
});
|
|
1552
1574
|
export type ManifestAgent = z.input<typeof manifestAgentSchema>;
|
|
1553
1575
|
|
|
1554
1576
|
/**
|
package/src/management/client.ts
CHANGED
|
@@ -166,6 +166,8 @@ export function inputSchemaOf(operation: OpenApiOperation) {
|
|
|
166
166
|
export function managementTarget(profile: { url: string; key: string }) {
|
|
167
167
|
return {
|
|
168
168
|
...profile,
|
|
169
|
+
// Every day range a command takes is this machine's days.
|
|
170
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
169
171
|
fetch: (url: string, init: RequestInit) =>
|
|
170
172
|
fetch(url, init).catch((error: unknown) => {
|
|
171
173
|
throw new UnreachableError(error);
|
package/src/scaffold/files.ts
CHANGED
|
@@ -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/
|
|
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
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
|
|
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
|
|
23
|
-
// package.json
|
|
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.
|
|
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.
|
|
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: "
|
|
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: "
|
|
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.
|
|
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,
|
|
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}},
|
|
292
|
-
#
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
"
|
|
556
|
+
".oxlintrc.json": oxlintrc,
|
|
551
557
|
"prettier.config.mjs": prettierConfig,
|
|
552
558
|
"cspell.json": cspellJson,
|
|
553
559
|
".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
|
-
]);
|