@m6d/cortex-cli 0.22.0 → 0.24.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/package.json +1 -1
- package/src/commands/management.ts +8 -5
- package/src/commands/setup.ts +15 -8
- package/src/contracts/management/procedures.generated.ts +792 -178
- package/src/contracts/manifest/index.ts +413 -97
- package/src/contracts/pattern/index.ts +276 -0
- package/src/contracts/runs/index.ts +187 -0
- package/src/contracts/schedule/index.ts +62 -0
- package/src/contracts/server/index.ts +99 -28
- package/src/contracts/triggers/index.ts +36 -93
- package/src/contracts/wire/index.ts +119 -55
- package/src/management/flags.ts +14 -4
- package/src/scaffold/files.ts +8 -3
- package/src/ui/prompts.ts +18 -24
package/src/management/flags.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type Flag = {
|
|
|
10
10
|
description: string;
|
|
11
11
|
/** A secret: never an argv flag (argv is world-readable), prompted masked or read from stdin. */
|
|
12
12
|
sensitive: boolean;
|
|
13
|
+
/** Takes `null`, which a blank prompted answer sends. */
|
|
14
|
+
nullable: boolean;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
/** Follows a local `$ref` into `components/schemas`; anything else is returned as is. */
|
|
@@ -36,6 +38,12 @@ function isSensitive(property: JsonSchema, document: OpenApiDocument): boolean {
|
|
|
36
38
|
);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
/** Whether `null` is allowed, as a type list or as zod's `anyOf` alternative for a nullable property. */
|
|
42
|
+
function isNullable(property: JsonSchema) {
|
|
43
|
+
if (Array.isArray(property.type)) return property.type.includes("null");
|
|
44
|
+
return (property.anyOf ?? []).some((alternative) => alternative.type === "null");
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
/** The flag kind a property's schema maps to; scalars and scalar arrays get typed flags, the rest is JSON. */
|
|
40
48
|
function kindOf(property: JsonSchema, document: OpenApiDocument) {
|
|
41
49
|
if (property.enum) return "enum" as const;
|
|
@@ -71,19 +79,21 @@ export function flagsFor(input: JsonSchema, document: OpenApiDocument) {
|
|
|
71
79
|
choices: property.enum?.map(String),
|
|
72
80
|
description: property.description ?? "",
|
|
73
81
|
sensitive: isSensitive(property, document),
|
|
82
|
+
nullable: isNullable(property),
|
|
74
83
|
} satisfies Flag;
|
|
75
84
|
});
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
/**
|
|
79
88
|
* A prompted answer, parsed and checked exactly as the flag's option would
|
|
80
|
-
* be; `undefined` when it does not fit, so the caller can ask again.
|
|
89
|
+
* be; `undefined` when it does not fit, so the caller can ask again. A blank
|
|
90
|
+
* answer is `null` for a nullable flag.
|
|
81
91
|
*/
|
|
82
92
|
export function parseAnswer(flag: Flag, answer: string) {
|
|
83
|
-
// A
|
|
84
|
-
|
|
93
|
+
// A secret's whitespace is part of it; any other answer of only spaces is blank.
|
|
94
|
+
if ((flag.sensitive ? answer : answer.trim()) === "") return flag.nullable ? null : undefined;
|
|
95
|
+
// A string is taken verbatim. Every other kind is lexical and trimmed before parsing.
|
|
85
96
|
const text = flag.kind === "string" ? answer : answer.trim();
|
|
86
|
-
if (text === "") return undefined;
|
|
87
97
|
return ANSWER_PARSERS[flag.kind](text, flag);
|
|
88
98
|
}
|
|
89
99
|
|
package/src/scaffold/files.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { applyPorts, type Ports } from "@/scaffold/ports";
|
|
|
28
28
|
* `@m6d/cortex` range below are stamped by scripts/sync-package-versions.ts
|
|
29
29
|
* before the CLI publishes, so a scaffold pins what was current then.
|
|
30
30
|
*/
|
|
31
|
-
const CORTEX_VERSION = "0.
|
|
31
|
+
const CORTEX_VERSION = "0.23.0";
|
|
32
32
|
|
|
33
33
|
/** This machine as Cortex's containers name it: the one private host the stack's egress policy admits. */
|
|
34
34
|
export const DOCKER_HOST = "host.docker.internal";
|
|
@@ -147,7 +147,7 @@ function packageJson({ name, datasets, setup }: Project) {
|
|
|
147
147
|
verify: "bun run format:check && bun run lint && bun run spellcheck && bun run check && bun run audit",
|
|
148
148
|
},
|
|
149
149
|
dependencies: {
|
|
150
|
-
"@m6d/cortex": "~0.
|
|
150
|
+
"@m6d/cortex": "~0.11.0",
|
|
151
151
|
// drizzle 1.0 is in beta; this is the build `@m6d/cortex/drizzle` is tested against.
|
|
152
152
|
...(datasets ? { "drizzle-orm": "1.0.0-beta.15-859cf75" } : {}),
|
|
153
153
|
hono: "^4.12.3",
|
|
@@ -246,6 +246,9 @@ COMPOSE_PROJECT_NAME=
|
|
|
246
246
|
DEFAULT_ADMIN_EMAIL=admin@example.com
|
|
247
247
|
DEFAULT_ADMIN_PASSWORD=
|
|
248
248
|
BOOTSTRAP_MANAGEMENT_KEY=
|
|
249
|
+
# The Cortex license key you were given. Without one Cortex serves no console
|
|
250
|
+
# and runs no agent.
|
|
251
|
+
CORTEX_LICENSE_KEY=
|
|
249
252
|
# Session signing, the key the secrets store is encrypted with, and object
|
|
250
253
|
# storage's password (storage is published on this machine for uploads).
|
|
251
254
|
BETTER_AUTH_SECRET=
|
|
@@ -497,7 +500,9 @@ A backend for Cortex agents, and Cortex itself to run them on.
|
|
|
497
500
|
|
|
498
501
|
## Getting started
|
|
499
502
|
|
|
500
|
-
On a fresh clone, run \`bun install\` first (\`cortex new\` already did): it writes \`.env\`.
|
|
503
|
+
On a fresh clone, run \`bun install\` first (\`cortex new\` already did): it writes \`.env\`. Put
|
|
504
|
+
your Cortex license key in its \`CORTEX_LICENSE_KEY\`: without one Cortex serves no console and runs no
|
|
505
|
+
agent.
|
|
501
506
|
|
|
502
507
|
\`\`\`sh
|
|
503
508
|
bun run dev
|
package/src/ui/prompts.ts
CHANGED
|
@@ -84,40 +84,34 @@ export async function askToProceed(message: string) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
88
|
-
* empty
|
|
87
|
+
* How an asked value is checked: re-asked while `check` complains about it,
|
|
88
|
+
* and while empty unless `blank` accepts an empty answer.
|
|
89
89
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
90
|
+
type AskOptions = { check?: (value: string) => string | undefined; blank?: boolean };
|
|
91
|
+
|
|
92
|
+
function validator(name: string, { check = () => undefined, blank = false }: AskOptions) {
|
|
93
|
+
return function (value: string | undefined) {
|
|
94
|
+
if (value) return check(value);
|
|
95
|
+
return blank ? undefined : `${name} is required.`;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** A value a command was not given: asked by name and checked trimmed. */
|
|
100
|
+
export function askValue(name: string, { hint, ...options }: AskOptions & { hint?: string } = {}) {
|
|
101
|
+
const validate = validator(name, options);
|
|
95
102
|
return orExit(
|
|
96
103
|
text({
|
|
97
104
|
message: name,
|
|
98
105
|
placeholder: hint,
|
|
99
106
|
output,
|
|
100
|
-
validate:
|
|
101
|
-
const candidate = (value ?? "").trim();
|
|
102
|
-
return candidate.length === 0 ? `${name} is required.` : check(candidate);
|
|
103
|
-
},
|
|
107
|
+
validate: (value) => validate(value?.trim()),
|
|
104
108
|
}),
|
|
105
109
|
);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
/** A credential: typed masked, never echoed, and
|
|
109
|
-
export function askSecret(
|
|
110
|
-
name:
|
|
111
|
-
check: (value: string) => string | undefined = () => undefined,
|
|
112
|
-
) {
|
|
113
|
-
return orExit(
|
|
114
|
-
password({
|
|
115
|
-
message: name,
|
|
116
|
-
output,
|
|
117
|
-
validate: (value) =>
|
|
118
|
-
(value ?? "").length === 0 ? `${name} is required.` : check(value ?? ""),
|
|
119
|
-
}),
|
|
120
|
-
);
|
|
112
|
+
/** A credential: typed masked, never echoed, and checked as typed. */
|
|
113
|
+
export function askSecret(name: string, options: AskOptions = {}) {
|
|
114
|
+
return orExit(password({ message: name, output, validate: validator(name, options) }));
|
|
121
115
|
}
|
|
122
116
|
|
|
123
117
|
type Step = { title: string; run: () => string | undefined | Promise<string | undefined> };
|