@nseng-ai/kernel 0.1.2

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.
@@ -0,0 +1,342 @@
1
+ import { optionalEntries } from "@nseng-ai/foundation/primitives";
2
+ import { z } from "zod";
3
+
4
+ import {
5
+ failed,
6
+ type ClinkrExit,
7
+ type NsCommand,
8
+ type NsCommandSchema,
9
+ type NsExtensionApi,
10
+ type NsResult,
11
+ } from "../sdk/index.ts";
12
+
13
+ import { extensionPointCommand, extensionPointsCommand } from "./built-in-extension-commands.ts";
14
+ import { classifyZodIssuePath, type ZodIssuePathRule } from "./zod-issue-path.ts";
15
+
16
+ export type NsCommandSourceLevel = "built-in" | "preinstalled" | "global" | "project";
17
+
18
+ export interface NsCommandPath {
19
+ group?: string;
20
+ name: string;
21
+ segments?: readonly string[];
22
+ groupDescription?: string;
23
+ }
24
+
25
+ export interface NsCommandSourceInfo {
26
+ level: NsCommandSourceLevel;
27
+ label: string;
28
+ path?: string;
29
+ }
30
+
31
+ export interface NsCommandInfo extends NsCommandPath {
32
+ description: string;
33
+ }
34
+
35
+ export interface NsCommandCliInfo extends NsCommandInfo {
36
+ fullDescription: string;
37
+ groupDescription?: string;
38
+ }
39
+
40
+ export interface NsCommandCandidate extends NsCommandCliInfo {
41
+ source: NsCommandSourceInfo;
42
+ entryPath?: string;
43
+ }
44
+
45
+ export interface BuiltInNsCommandCandidate extends NsCommandCandidate {
46
+ source: NsCommandSourceInfo & { level: "built-in" };
47
+ command: NsCommand;
48
+ }
49
+
50
+ export interface BuiltInCommandDefinition extends Partial<NsCommandPath> {
51
+ command: NsCommand;
52
+ }
53
+
54
+ export const builtInCommandDefinitions: Readonly<Record<string, BuiltInCommandDefinition>> = {
55
+ "extension/point": {
56
+ name: "point",
57
+ segments: ["extension", "point"],
58
+ groupDescription: "Inspect ns extension metadata.",
59
+ command: extensionPointCommand,
60
+ },
61
+ "extension/points": {
62
+ name: "points",
63
+ segments: ["extension", "points"],
64
+ groupDescription: "Inspect ns extension metadata.",
65
+ command: extensionPointsCommand,
66
+ },
67
+ };
68
+
69
+ const nsCommandSchema = z.object({
70
+ name: z.string(),
71
+ summary: z.string(),
72
+ description: z.string(),
73
+ schema: z.custom<NsCommandSchema>(isZodObjectSchema).optional(),
74
+ positionals: z.custom<NsCommand["positionals"]>(isRecord).optional(),
75
+ options: z.custom<NsCommand["options"]>(isRecord).optional(),
76
+ resultSchema: z.custom<NsCommand["resultSchema"]>(isZodSchema).optional(),
77
+ renderHuman: z
78
+ .custom<NsCommand["renderHuman"]>((value) => typeof value === "function")
79
+ .optional(),
80
+ renderMarkdown: z
81
+ .custom<NsCommand["renderMarkdown"]>((value) => typeof value === "function")
82
+ .optional(),
83
+ completionProvider: z
84
+ .custom<NsCommand["completionProvider"]>((value) => typeof value === "function")
85
+ .optional(),
86
+ run: z.custom<NsCommand["run"]>((value) => typeof value === "function"),
87
+ });
88
+
89
+ const nsExtensionSchema = z.object({
90
+ commands: z.array(nsCommandSchema).optional().default([]),
91
+ });
92
+
93
+ const nsResultSchema = z.discriminatedUnion("ok", [
94
+ z.object({ ok: z.literal(true), message: z.string() }),
95
+ z.object({ ok: z.literal(false), exitCode: z.number(), message: z.string() }),
96
+ ]);
97
+
98
+ export function commandSegments(path: NsCommandPath): readonly string[] {
99
+ if (path.segments !== undefined) return path.segments;
100
+ return path.group === undefined ? [path.name] : [path.group, path.name];
101
+ }
102
+
103
+ export function commandKey(path: NsCommandPath): string {
104
+ return commandSegments(path).join("/");
105
+ }
106
+
107
+ export function commandLeafName(path: NsCommandPath): string {
108
+ return path.segments?.at(-1) ?? path.name;
109
+ }
110
+
111
+ export function commandDisplayName(path: NsCommandPath): string {
112
+ return commandSegments(path).join(" ");
113
+ }
114
+
115
+ export function commandPathMatches(left: NsCommandPath, right: NsCommandPath): boolean {
116
+ return commandKey(left) === commandKey(right);
117
+ }
118
+
119
+ export function listBuiltInNsCommandCandidates(): BuiltInNsCommandCandidate[] {
120
+ return Object.entries(builtInCommandDefinitions)
121
+ .map(([name, definition]) => ({
122
+ name: definition.name ?? name,
123
+ ...optionalEntries({
124
+ group: definition.group,
125
+ segments: definition.segments,
126
+ groupDescription: definition.groupDescription,
127
+ }),
128
+ description: definition.command.summary,
129
+ fullDescription: definition.command.description,
130
+ source: { level: "built-in" as const, label: `built-in command ${name}` },
131
+ command: definition.command,
132
+ }))
133
+ .sort((left, right) => commandKey(left).localeCompare(commandKey(right)));
134
+ }
135
+
136
+ export function listStaticNsCommandInfos(): NsCommandCliInfo[] {
137
+ return listBuiltInNsCommandCandidates().map(toCommandCliInfo);
138
+ }
139
+
140
+ export function toCommandCliInfo(
141
+ candidate: NsCommandPath & Pick<NsCommandCliInfo, "description" | "fullDescription">,
142
+ ): NsCommandCliInfo {
143
+ return {
144
+ ...optionalEntries({
145
+ group: candidate.group,
146
+ segments: candidate.segments,
147
+ groupDescription: candidate.groupDescription,
148
+ }),
149
+ name: candidate.name,
150
+ description: candidate.description,
151
+ fullDescription: candidate.fullDescription,
152
+ };
153
+ }
154
+
155
+ export function commandInfoForLoadedCommand(
156
+ command: NsCommand,
157
+ sourceLevel: NsCommandSourceLevel,
158
+ path: NsCommandPath,
159
+ ): NsCommandCliInfo {
160
+ const definition = path.group === undefined ? builtInCommandDefinitions[command.name] : undefined;
161
+ if (sourceLevel === "built-in" && definition !== undefined) {
162
+ return {
163
+ name: command.name,
164
+ description: definition.command.summary,
165
+ fullDescription: definition.command.description,
166
+ };
167
+ }
168
+ return toCommandCliInfo({
169
+ ...path,
170
+ name: command.name,
171
+ description: command.summary,
172
+ fullDescription: command.description,
173
+ });
174
+ }
175
+
176
+ export function validateNsExtensionContribution(
177
+ contribution: unknown,
178
+ expectedPath: NsCommandPath | string,
179
+ sourceLabel: string,
180
+ ): { ok: true; command: NsCommand } | { ok: false; message: string } {
181
+ const expectedName =
182
+ typeof expectedPath === "string" ? expectedPath : commandLeafName(expectedPath);
183
+ const parsed = nsExtensionSchema.safeParse(contribution);
184
+ if (!parsed.success) {
185
+ return {
186
+ ok: false,
187
+ message: `Invalid ns extension contribution ${sourceLabel}: ${formatNsExtensionIssue(parsed.error.issues[0])}`,
188
+ };
189
+ }
190
+
191
+ const command = findCommandEntry(parsed.data, expectedName);
192
+ if (command === undefined) {
193
+ return {
194
+ ok: false,
195
+ message: `Invalid ns extension contribution ${sourceLabel}: expected a command entry named "${expectedName}" in commands[].`,
196
+ };
197
+ }
198
+
199
+ return { ok: true, command };
200
+ }
201
+
202
+ export async function executeNsCommand(
203
+ ctx: NsExtensionApi,
204
+ command: NsCommand,
205
+ request: unknown,
206
+ ): Promise<NsResult> {
207
+ const parsedRequest = (command.schema ?? z.object({})).safeParse(request);
208
+ if (!parsedRequest.success) {
209
+ return failed(
210
+ `Invalid request for command ${command.name}: ${parsedRequest.error.issues[0]?.message ?? "request did not match command schema"}`,
211
+ 2,
212
+ );
213
+ }
214
+
215
+ try {
216
+ const result = await command.run(ctx, parsedRequest.data);
217
+ return validateNsResult(result, command.name);
218
+ } catch (error) {
219
+ return failed(`Command ${command.name} failed.\n${formatUnknownError(error)}`, 2);
220
+ }
221
+ }
222
+
223
+ export function validateNsResult(result: unknown, commandName: string): NsResult {
224
+ const parsed = nsResultSchema.safeParse(result);
225
+ if (parsed.success) {
226
+ return parsed.data;
227
+ }
228
+
229
+ if (hasInvalidFailureExitCode(parsed.error.issues)) {
230
+ return failed(`Command ${commandName} returned an invalid failure result.`, 2);
231
+ }
232
+ return failed(`Command ${commandName} returned an invalid result.`, 2);
233
+ }
234
+
235
+ export function validateNsClinkrExit(result: unknown, commandName: string): ClinkrExit<unknown> {
236
+ if (isNsClinkrExit(result)) return result;
237
+ return {
238
+ type: "failure",
239
+ errorType: "invalid-extension-result",
240
+ message: `Command ${commandName} returned an invalid rendered result.`,
241
+ };
242
+ }
243
+
244
+ export function formatUnknownError(error: unknown): string {
245
+ return error instanceof Error ? error.message : String(error);
246
+ }
247
+
248
+ function findCommandEntry(
249
+ extension: { commands: readonly NsCommand[] },
250
+ expectedName: string,
251
+ ): NsCommand | undefined {
252
+ return extension.commands.find((command) => command.name === expectedName);
253
+ }
254
+
255
+ const nsExtensionCommandEntryIssueFields = [
256
+ { field: "name", message: "command name must be a string" },
257
+ { field: "summary", message: "command summary must be a string" },
258
+ { field: "description", message: "command description must be a string" },
259
+ {
260
+ field: "schema",
261
+ message: "command schema must be a Zod object schema from @nseng-ai/kernel/sdk",
262
+ },
263
+ { field: "options", message: "command options must be an object" },
264
+ { field: "completionProvider", message: "command completionProvider must be a function" },
265
+ { field: "run", message: "command run must be a function" },
266
+ ] as const satisfies readonly { field: string; message: string }[];
267
+
268
+ type NsExtensionCommandEntryIssueField =
269
+ (typeof nsExtensionCommandEntryIssueFields)[number]["field"];
270
+
271
+ type NsExtensionIssueKind =
272
+ | "invalid-extension"
273
+ | "commands-not-array"
274
+ | NsExtensionCommandEntryIssueField
275
+ | "entry-other";
276
+
277
+ const nsExtensionIssueRules: readonly ZodIssuePathRule<NsExtensionIssueKind>[] = [
278
+ { pattern: ["commands"], match: "exact", value: "commands-not-array" },
279
+ ...nsExtensionCommandEntryIssueFields.map(
280
+ ({ field }) =>
281
+ ({
282
+ pattern: ["commands", { type: "number" }, field],
283
+ match: "exact",
284
+ value: field,
285
+ }) satisfies ZodIssuePathRule<NsExtensionIssueKind>,
286
+ ),
287
+ { pattern: ["commands"], match: "prefix", value: "entry-other" },
288
+ ];
289
+
290
+ function formatNsExtensionIssue(issue: z.core.$ZodIssue | undefined): string {
291
+ const kind = classifyZodIssuePath(issue, nsExtensionIssueRules, "invalid-extension");
292
+ if (kind === "invalid-extension") {
293
+ return "default export must be an extension object created with defineExtension().";
294
+ }
295
+ if (kind === "commands-not-array") {
296
+ return "ns extension commands must be an array of command entries.";
297
+ }
298
+ return `Invalid ns command entry in extension: ${formatNsCommandEntryIssueKind(kind)}.`;
299
+ }
300
+
301
+ function formatNsCommandEntryIssueKind(
302
+ kind: Exclude<NsExtensionIssueKind, "invalid-extension" | "commands-not-array">,
303
+ ): string {
304
+ const entry = nsExtensionCommandEntryIssueFields.find((field) => field.field === kind);
305
+ if (entry !== undefined) return entry.message;
306
+ return "command entry must include name, summary, description, and run";
307
+ }
308
+
309
+ function isZodObjectSchema(value: unknown): value is NsCommandSchema {
310
+ if (value instanceof z.ZodObject) return true;
311
+ if (!isRecord(value)) return false;
312
+ const candidate = value as { safeParse?: unknown; _zod?: { def?: { type?: unknown } } };
313
+ return typeof candidate.safeParse === "function" && candidate._zod?.def?.type === "object";
314
+ }
315
+
316
+ function isZodSchema(value: unknown): value is z.ZodType {
317
+ if (value instanceof z.ZodType) return true;
318
+ if (!isRecord(value)) return false;
319
+ const candidate = value as { safeParse?: unknown; _zod?: { def?: unknown } };
320
+ return typeof candidate.safeParse === "function" && candidate._zod?.def !== undefined;
321
+ }
322
+
323
+ function isNsClinkrExit(value: unknown): value is ClinkrExit<unknown> {
324
+ if (!isRecord(value)) return false;
325
+ if (value.type === "ok") return "data" in value;
326
+ if (value.type === "negative") return typeof value.message === "string";
327
+ if (value.type === "failure") {
328
+ return typeof value.errorType === "string" && typeof value.message === "string";
329
+ }
330
+ if (value.type === "usageError") {
331
+ return value.errorType === "usageError" && typeof value.message === "string";
332
+ }
333
+ return false;
334
+ }
335
+
336
+ function isRecord(value: unknown): value is Record<string, unknown> {
337
+ return typeof value === "object" && value !== null && !Array.isArray(value);
338
+ }
339
+
340
+ function hasInvalidFailureExitCode(issues: readonly z.core.$ZodIssue[]): boolean {
341
+ return issues.some((issue) => issue.path.length === 1 && issue.path[0] === "exitCode");
342
+ }