@m6d/cortex-cli 0.18.0 → 0.19.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),
@@ -1536,9 +1520,34 @@ const manifestAgentSchema = z.strictObject({
1536
1520
  })
1537
1521
  .default({}),
1538
1522
  settings: agentSettingsSchema.prefault({}),
1523
+ /** Messages a minute and model tokens a UTC day, for all users together and for each; null, no limit. */
1524
+ rateLimits: z
1525
+ .strictObject({
1526
+ allUsers: z
1527
+ .strictObject({
1528
+ messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "allUsers"),
1529
+ tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "allUsers"),
1530
+ })
1531
+ .prefault({}),
1532
+ perUser: z
1533
+ .strictObject({
1534
+ messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "perUser"),
1535
+ tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "perUser"),
1536
+ })
1537
+ .prefault({}),
1538
+ })
1539
+ .prefault({}),
1539
1540
  suggestions: suggestionsSchema.default(() => byLocale(() => [])),
1540
1541
  greeting: greetingSchema.default(() => byLocale(() => "")),
1541
- overridable: overridable(["name", "prompt", "models", "settings", "suggestions", "greeting"]),
1542
+ overridable: overridable([
1543
+ "name",
1544
+ "prompt",
1545
+ "models",
1546
+ "settings",
1547
+ "rateLimits",
1548
+ "suggestions",
1549
+ "greeting",
1550
+ ]),
1542
1551
  });
1543
1552
  export type ManifestAgent = z.input<typeof manifestAgentSchema>;
1544
1553
 
@@ -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,28 @@ 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
+ fetch: (url: string, init: RequestInit) =>
170
+ fetch(url, init).catch((error: unknown) => {
171
+ throw new UnreachableError(error);
172
+ }),
173
+ } satisfies ManagementTarget;
207
174
  }
208
175
 
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}`;
176
+ /** The typed client for the calls the CLI makes by name, over `managementTarget`. */
177
+ export function managementClient(profile: { url: string; key: string }) {
178
+ return createManagementClient(managementTarget(profile));
221
179
  }
222
180
 
223
181
  /**
224
182
  * Cortex did not answer at all: refused, reset, unresolvable. `fetch` rejects
225
183
  * only for these, with whatever its runtime throws (Bun's is a plain `Error`
226
- * with a `code`), so `callManagement` names them once here.
184
+ * with a `code`), so `managementTarget` names them once here.
227
185
  */
228
186
  export class UnreachableError extends Error {
229
187
  public constructor(cause: unknown) {
@@ -231,21 +189,3 @@ export class UnreachableError extends Error {
231
189
  this.name = "UnreachableError";
232
190
  }
233
191
  }
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
- }
@@ -29,7 +29,7 @@ import { applyPorts, type Ports } from "@/scaffold/ports";
29
29
  * `@m6d/cortex` range below are stamped by scripts/sync-package-versions.ts
30
30
  * before the CLI publishes, so a scaffold pins what was current then.
31
31
  */
32
- const CORTEX_VERSION = "0.17.0";
32
+ const CORTEX_VERSION = "0.18.0";
33
33
 
34
34
  /** This machine as Cortex's containers name it: the one private host the stack's egress policy admits. */
35
35
  export const DOCKER_HOST = "host.docker.internal";
@@ -150,7 +150,7 @@ function packageJson({ name, datasets, setup }: Project) {
150
150
  verify: "bun run format:check && bun run lint && bun run spellcheck && bun run check && bun run audit",
151
151
  },
152
152
  dependencies: {
153
- "@m6d/cortex": "~0.5.0",
153
+ "@m6d/cortex": "~0.6.0",
154
154
  // drizzle 1.0 is in beta; this is the build `@m6d/cortex/drizzle` is tested against.
155
155
  ...(datasets ? { "drizzle-orm": "1.0.0-beta.15-859cf75" } : {}),
156
156
  hono: "^4.12.3",