@procedures/mark 0.34.0 → 0.35.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/CHANGELOG.md +30 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/args.d.ts +5 -53
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +9 -350
- package/dist/args.js.map +1 -1
- package/dist/ask.d.ts +3 -38
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +3 -81
- package/dist/ask.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +18 -64
- package/dist/cli.js.map +1 -1
- package/dist/failure.d.ts +3 -2
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +3 -33
- package/dist/failure.js.map +1 -1
- package/dist/format.d.ts +3 -25
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +3 -120
- package/dist/format.js.map +1 -1
- package/dist/manifest.json +19 -19
- package/dist/parse.d.ts +4 -114
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +7 -445
- package/dist/parse.js.map +1 -1
- package/package.json +50 -50
- package/src/args.ts +14 -411
- package/src/ask.ts +14 -109
- package/src/cli.ts +16 -70
- package/src/failure.ts +3 -33
- package/src/format.ts +3 -172
- package/src/parse.ts +33 -570
- package/dist/prompts.d.ts +0 -58
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -124
- package/dist/prompts.js.map +0 -1
- package/src/prompts.ts +0 -181
package/src/ask.ts
CHANGED
|
@@ -6,114 +6,19 @@
|
|
|
6
6
|
* when stdin or stdout is not a terminal, when the environment variable CI is set, with
|
|
7
7
|
* `--no-input`, or when the procedure sets `interactive: false` in its CLI metadata. Then a
|
|
8
8
|
* missing argument is an error of the input, as before.
|
|
9
|
+
*
|
|
10
|
+
* The code is in `@procedures/cli-kit` (roadmap O61).
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
env: Record<string, string | undefined>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function processTerminal(): Terminal {
|
|
27
|
-
return {
|
|
28
|
-
stdinIsTTY: process.stdin.isTTY === true,
|
|
29
|
-
stdoutIsTTY: process.stdout.isTTY === true,
|
|
30
|
-
env: process.env,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** True when mark can ask for a missing argument */
|
|
35
|
-
export function canAsk(noInput: boolean, meta: CLIMeta, terminal: Terminal): boolean {
|
|
36
|
-
if (noInput || meta.interactive === false) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
const ci = terminal.env["CI"];
|
|
40
|
-
if (ci !== undefined && ci !== "" && ci !== "false" && ci !== "0") {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
return terminal.stdinIsTTY && terminal.stdoutIsTTY;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** The required fields that the input does not give (a field with a default is not missing) */
|
|
47
|
-
export function missingFields(fields: readonly SchemaFieldInfo[], input: Record<string, unknown>): SchemaFieldInfo[] {
|
|
48
|
-
return fields.filter((field) => field.required && field.defaultValue === undefined && input[field.name] === undefined);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** The question of a field: its name, its type and its description */
|
|
52
|
-
export function questionOf(field: SchemaFieldInfo): string {
|
|
53
|
-
const many = field.fieldType.kind === "array" ? " (separate the values with spaces)" : "";
|
|
54
|
-
const description = field.description ? `: ${field.description}` : "";
|
|
55
|
-
return `${field.name} (${field.type})${description}${many}`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** The result of the questions: the input with the answers, or the reason to stop */
|
|
59
|
-
export type Asked =
|
|
60
|
-
| { ok: true; input: Record<string, unknown> }
|
|
61
|
-
| { ok: false; cancelled: boolean; error: string };
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Ask for each missing field, in order, and convert each answer by the type of its field. An
|
|
65
|
-
* array field takes the words of the answer.
|
|
66
|
-
*/
|
|
67
|
-
export async function askMissing(
|
|
68
|
-
missing: readonly SchemaFieldInfo[],
|
|
69
|
-
input: Record<string, unknown>,
|
|
70
|
-
ask: Asker
|
|
71
|
-
): Promise<Asked> {
|
|
72
|
-
const result = { ...input };
|
|
73
|
-
for (const field of missing) {
|
|
74
|
-
const answer = await ask(field, questionOf(field));
|
|
75
|
-
if ("cancelled" in answer) {
|
|
76
|
-
return { ok: false, cancelled: true, error: "Cancelled" };
|
|
77
|
-
}
|
|
78
|
-
if (typeof answer.value !== "string") {
|
|
79
|
-
result[field.name] = answer.value;
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
const kind = field.fieldType.kind;
|
|
83
|
-
if (kind === "array") {
|
|
84
|
-
const words = answer.value.trim().split(/\s+/).filter((word) => word.length > 0);
|
|
85
|
-
const item = field.fieldType.item ?? { kind: "any" as const };
|
|
86
|
-
const values: unknown[] = [];
|
|
87
|
-
for (const word of words) {
|
|
88
|
-
const converted = convertValue(word, item, field.name);
|
|
89
|
-
if (!converted.ok) return { ok: false, cancelled: false, error: converted.error };
|
|
90
|
-
values.push(converted.value);
|
|
91
|
-
}
|
|
92
|
-
result[field.name] = values;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
const converted = convertValue(answer.value, field.fieldType, field.name);
|
|
96
|
-
if (!converted.ok) {
|
|
97
|
-
return { ok: false, cancelled: false, error: converted.error };
|
|
98
|
-
}
|
|
99
|
-
result[field.name] = converted.value;
|
|
100
|
-
}
|
|
101
|
-
return { ok: true, input: result };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** The asker of the terminal: a yes/no question for a boolean, a choice for an enum, text otherwise */
|
|
105
|
-
export const terminalAsker: Asker = async (field, message) => {
|
|
106
|
-
const { prompts } = await import("./prompts.js");
|
|
107
|
-
let value: unknown;
|
|
108
|
-
if (field.fieldType.kind === "boolean") {
|
|
109
|
-
value = await prompts.confirm(message);
|
|
110
|
-
} else if (field.fieldType.kind === "enum" && field.enumValues) {
|
|
111
|
-
value = await prompts.select(
|
|
112
|
-
message,
|
|
113
|
-
field.enumValues.map((option) => ({ value: option, label: option }))
|
|
114
|
-
);
|
|
115
|
-
} else {
|
|
116
|
-
value = await prompts.text(message);
|
|
117
|
-
}
|
|
118
|
-
return prompts.isCancel(value) ? { cancelled: true } : { value };
|
|
119
|
-
};
|
|
13
|
+
export {
|
|
14
|
+
askMissing,
|
|
15
|
+
canAsk,
|
|
16
|
+
missingFields,
|
|
17
|
+
processTerminal,
|
|
18
|
+
questionOf,
|
|
19
|
+
terminalAsker,
|
|
20
|
+
type Answer,
|
|
21
|
+
type Asked,
|
|
22
|
+
type Asker,
|
|
23
|
+
type Terminal,
|
|
24
|
+
} from "@procedures/cli-kit";
|
package/src/cli.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { connectCliServer } from "./client-mode.js";
|
|
|
22
22
|
import type { ColdCommand, CommandRun } from "./command.js";
|
|
23
23
|
import { startRepl } from "./repl.js";
|
|
24
24
|
import { findChildren, findProcedure, parseCommandLine, parseLeadingGlobals } from "./args.js";
|
|
25
|
+
import { groupHelp, rootHelp, validationMessage } from "@procedures/cli-kit";
|
|
25
26
|
import { failureOf } from "./failure.js";
|
|
26
27
|
|
|
27
28
|
/** The version of this package. This file is `dist/cli.js` (or `src/cli.ts` in the tests): the package.json is one folder up. */
|
|
@@ -50,19 +51,6 @@ function fail(message: string, hint?: string): void {
|
|
|
50
51
|
process.exitCode = 1;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
/**
|
|
54
|
-
* The message of a validation error: one line for each issue of a Zod error
|
|
55
|
-
*/
|
|
56
|
-
function validationMessage(error: unknown): string {
|
|
57
|
-
const issues = (error as { issues?: Array<{ path?: unknown[]; message?: string }> }).issues;
|
|
58
|
-
if (Array.isArray(issues) && issues.length > 0) {
|
|
59
|
-
return issues
|
|
60
|
-
.map((issue) => `${(issue.path ?? []).join(".") || "input"}: ${issue.message ?? "invalid"}`)
|
|
61
|
-
.join("\n ");
|
|
62
|
-
}
|
|
63
|
-
return error instanceof Error ? error.message : String(error);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
54
|
/**
|
|
67
55
|
* Show help for a path (either a command or a group). Returns false for an unknown path.
|
|
68
56
|
*/
|
|
@@ -77,33 +65,9 @@ function showHelp(procedures: readonly CommandLike[], path: string[]): boolean {
|
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
// Show help for group
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
82
|
-
const
|
|
83
|
-
print.info(`${groupName} commands:\n`);
|
|
84
|
-
|
|
85
|
-
// Direct children only
|
|
86
|
-
const directChildren = children.filter((p) => p.path.length === path.length + 1);
|
|
87
|
-
for (const child of directChildren) {
|
|
88
|
-
const meta = (child.metadata ?? {}) as CLIMeta;
|
|
89
|
-
const cmdName = child.path[child.path.length - 1] ?? "";
|
|
90
|
-
const desc = meta.description ?? "";
|
|
91
|
-
print.info(` mark ${groupName} ${cmdName} ${desc}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Child groups
|
|
95
|
-
const childGroups = new Set<string>();
|
|
96
|
-
for (const child of children) {
|
|
97
|
-
if (child.path.length > path.length + 1) {
|
|
98
|
-
const groupSeg = child.path[path.length];
|
|
99
|
-
if (groupSeg) childGroups.add(groupSeg);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
for (const group of childGroups) {
|
|
103
|
-
print.info(` mark ${groupName} ${group} ${group} commands`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
print.info(`\nRun 'mark ${groupName} <command> --help' for more info.`);
|
|
68
|
+
const group = groupHelp("mark", procedures, path);
|
|
69
|
+
if (group) {
|
|
70
|
+
for (const line of group) print.info(line);
|
|
107
71
|
return true;
|
|
108
72
|
}
|
|
109
73
|
|
|
@@ -116,36 +80,18 @@ function showHelp(procedures: readonly CommandLike[], path: string[]): boolean {
|
|
|
116
80
|
* Show root help
|
|
117
81
|
*/
|
|
118
82
|
function showRootHelp(procedures: readonly CommandLike[]): void {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
for (const [group, procs] of groups) {
|
|
133
|
-
print.info(` ${group}`);
|
|
134
|
-
for (const proc of procs) {
|
|
135
|
-
const meta = (proc.metadata ?? {}) as CLIMeta;
|
|
136
|
-
const cmdPath = proc.path.join(" ");
|
|
137
|
-
const desc = meta.description ?? "";
|
|
138
|
-
print.info(` mark ${cmdPath} ${desc}`);
|
|
139
|
-
}
|
|
140
|
-
print.info("");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
print.info("Run 'mark <command> --help' for more information.");
|
|
144
|
-
print.info("Hosts: 'mark serve [--procedures pkgs] [--port N] [--expose patterns] [--events topics]' (the procedure server),");
|
|
145
|
-
print.info(" 'mark mcp' (the dev-tools MCP server over stdio).");
|
|
146
|
-
print.info("Modes: 'mark --server [--port N] [--host H] [--transport http|websocket|both] [--events topics] [--probe-port N]' and 'mark -i' (the REPL).");
|
|
147
|
-
print.info("Root: 'mark --fs-root <folder> <command>' keeps each path of each procedure in the folder, also in each mode and host.");
|
|
148
|
-
print.info(" It refuses the procedures that it cannot confine: git, pnpm, docker run, a program (FORBIDDEN, outside-root).");
|
|
83
|
+
const lines = rootHelp(procedures, {
|
|
84
|
+
name: "mark",
|
|
85
|
+
title: `mark v${VERSION} - Development workflow automation`,
|
|
86
|
+
footer: [
|
|
87
|
+
"Hosts: 'mark serve [--procedures pkgs] [--port N] [--expose patterns] [--events topics]' (the procedure server),",
|
|
88
|
+
" 'mark mcp' (the dev-tools MCP server over stdio).",
|
|
89
|
+
"Modes: 'mark --server [--port N] [--host H] [--transport http|websocket|both] [--events topics] [--probe-port N]' and 'mark -i' (the REPL).",
|
|
90
|
+
"Root: 'mark --fs-root <folder> <command>' keeps each path of each procedure in the folder, also in each mode and host.",
|
|
91
|
+
" It refuses the procedures that it cannot confine: git, pnpm, docker run, a program (FORBIDDEN, outside-root).",
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
for (const line of lines) print.info(line);
|
|
149
95
|
}
|
|
150
96
|
|
|
151
97
|
/** The global flags that only `--server` takes. */
|
package/src/failure.ts
CHANGED
|
@@ -10,38 +10,8 @@
|
|
|
10
10
|
* runs an external command (`vitest run`, `shell run`), so the caller sees the command's result.
|
|
11
11
|
*
|
|
12
12
|
* For a streaming procedure, each item is examined: one failed item makes the command fail.
|
|
13
|
+
*
|
|
14
|
+
* The code is in `@procedures/cli-kit` (roadmap O61).
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
export function failureOf(result: unknown): string | undefined {
|
|
17
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
const record = result as Record<string, unknown>;
|
|
21
|
-
const exitCode = record["exitCode"];
|
|
22
|
-
if (typeof exitCode === "number" && exitCode !== 0) {
|
|
23
|
-
return `exit code ${exitCode}`;
|
|
24
|
-
}
|
|
25
|
-
if (record["success"] === false) {
|
|
26
|
-
return firstText(record["error"]) ?? firstText(record["errors"]) ?? firstText(record["message"]) ?? "success: false";
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** The first text of a value: a string, the message of an error object, or the first of a list */
|
|
32
|
-
function firstText(value: unknown): string | undefined {
|
|
33
|
-
if (typeof value === "string" && value.length > 0) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
if (Array.isArray(value)) {
|
|
37
|
-
for (const item of value) {
|
|
38
|
-
const text = firstText(item);
|
|
39
|
-
if (text) return text;
|
|
40
|
-
}
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
if (value && typeof value === "object" && typeof (value as { message?: unknown }).message === "string") {
|
|
44
|
-
return (value as { message: string }).message;
|
|
45
|
-
}
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
17
|
+
export { failureOf } from "@procedures/cli-kit";
|
package/src/format.ts
CHANGED
|
@@ -1,177 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Output Formatting
|
|
3
3
|
*
|
|
4
|
-
* Formats procedure results for CLI output based on metadata hints.
|
|
4
|
+
* Formats procedure results for CLI output based on metadata hints. The code is in
|
|
5
|
+
* `@procedures/cli-kit` (roadmap O61).
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
export type OutputFormat
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Print interface (used by formatOutput)
|
|
11
|
-
*/
|
|
12
|
-
export interface Print {
|
|
13
|
-
info: (message: string) => void;
|
|
14
|
-
error: (message: string) => void;
|
|
15
|
-
success: (message: string) => void;
|
|
16
|
-
warning: (message: string) => void;
|
|
17
|
-
table: (data: string[][], options?: { format?: string }) => void;
|
|
18
|
-
spin: (message: string) => Spinner;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Spinner {
|
|
22
|
-
stop: () => void;
|
|
23
|
-
succeed: (message?: string) => void;
|
|
24
|
-
fail: (message?: string) => void;
|
|
25
|
-
text: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Format and print the result based on output format hint
|
|
30
|
-
*/
|
|
31
|
-
export function formatOutput(
|
|
32
|
-
print: Print,
|
|
33
|
-
result: unknown,
|
|
34
|
-
format: OutputFormat = "text"
|
|
35
|
-
): void {
|
|
36
|
-
if (result === undefined || result === null) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
switch (format) {
|
|
41
|
-
case "json":
|
|
42
|
-
print.info(JSON.stringify(result, null, 2));
|
|
43
|
-
break;
|
|
44
|
-
|
|
45
|
-
case "table":
|
|
46
|
-
formatTable(print, result);
|
|
47
|
-
break;
|
|
48
|
-
|
|
49
|
-
case "text":
|
|
50
|
-
case "streaming":
|
|
51
|
-
default:
|
|
52
|
-
formatText(print, result);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Format as text - handles common result shapes
|
|
59
|
-
*/
|
|
60
|
-
function formatText(print: Print, result: unknown): void {
|
|
61
|
-
// String output
|
|
62
|
-
if (typeof result === "string") {
|
|
63
|
-
print.info(result);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Result with output field (common pattern)
|
|
68
|
-
if (isObjectWithOutput(result)) {
|
|
69
|
-
print.info(result.output);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Result with message field
|
|
74
|
-
if (isObjectWithMessage(result)) {
|
|
75
|
-
if (result.success === false) {
|
|
76
|
-
print.error(result.message);
|
|
77
|
-
} else {
|
|
78
|
-
print.success(result.message);
|
|
79
|
-
}
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Result with error field
|
|
84
|
-
if (isObjectWithError(result)) {
|
|
85
|
-
print.error(result.error);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Array of items
|
|
90
|
-
if (Array.isArray(result)) {
|
|
91
|
-
for (const item of result) {
|
|
92
|
-
formatText(print, item);
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Fallback to JSON
|
|
98
|
-
print.info(JSON.stringify(result, null, 2));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Format as table - handles arrays of objects
|
|
103
|
-
*/
|
|
104
|
-
function formatTable(print: Print, result: unknown): void {
|
|
105
|
-
// Result with rows/headers
|
|
106
|
-
if (isTableResult(result)) {
|
|
107
|
-
const headers = result.headers ?? Object.keys(result.rows[0] ?? {});
|
|
108
|
-
const data: string[][] = [headers];
|
|
109
|
-
|
|
110
|
-
for (const row of result.rows) {
|
|
111
|
-
if (Array.isArray(row)) {
|
|
112
|
-
data.push(row.map(String));
|
|
113
|
-
} else if (typeof row === "object" && row !== null) {
|
|
114
|
-
data.push(headers.map((h) => String((row as Record<string, unknown>)[h] ?? "")));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
print.table(data);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Plain array of objects
|
|
123
|
-
if (Array.isArray(result) && result.length > 0 && typeof result[0] === "object") {
|
|
124
|
-
const headers = Object.keys(result[0] as object);
|
|
125
|
-
const data: string[][] = [headers];
|
|
126
|
-
|
|
127
|
-
for (const row of result) {
|
|
128
|
-
if (typeof row === "object" && row !== null) {
|
|
129
|
-
data.push(headers.map((h) => String((row as Record<string, unknown>)[h] ?? "")));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
print.table(data);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Fallback to text
|
|
138
|
-
formatText(print, result);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Type guards
|
|
142
|
-
|
|
143
|
-
function isObjectWithOutput(val: unknown): val is { output: string } {
|
|
144
|
-
return (
|
|
145
|
-
typeof val === "object" &&
|
|
146
|
-
val !== null &&
|
|
147
|
-
"output" in val &&
|
|
148
|
-
typeof (val as { output: unknown }).output === "string"
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function isObjectWithMessage(val: unknown): val is { message: string; success?: boolean } {
|
|
153
|
-
return (
|
|
154
|
-
typeof val === "object" &&
|
|
155
|
-
val !== null &&
|
|
156
|
-
"message" in val &&
|
|
157
|
-
typeof (val as { message: unknown }).message === "string"
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function isObjectWithError(val: unknown): val is { error: string } {
|
|
162
|
-
return (
|
|
163
|
-
typeof val === "object" &&
|
|
164
|
-
val !== null &&
|
|
165
|
-
"error" in val &&
|
|
166
|
-
typeof (val as { error: unknown }).error === "string"
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function isTableResult(val: unknown): val is { rows: unknown[]; headers?: string[] } {
|
|
171
|
-
return (
|
|
172
|
-
typeof val === "object" &&
|
|
173
|
-
val !== null &&
|
|
174
|
-
"rows" in val &&
|
|
175
|
-
Array.isArray((val as { rows: unknown }).rows)
|
|
176
|
-
);
|
|
177
|
-
}
|
|
8
|
+
export { formatOutput, type OutputFormat, type Print, type Spinner } from "@procedures/cli-kit";
|