@ekanos/cli 0.3.3 → 0.3.4
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/dist/auth/session.js +13 -4
- package/dist/auth/session.js.map +1 -1
- package/dist/banner.d.ts +1 -1
- package/dist/banner.js +4 -13
- package/dist/banner.js.map +1 -1
- package/dist/commands/dev.js +11 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +68 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +25 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +30 -4
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/publish.js +25 -12
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/sources.js +26 -16
- package/dist/commands/sources.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.js +52 -39
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/test.js +15 -5
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/upgrade.js +85 -53
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/use.js +10 -2
- package/dist/commands/use.js.map +1 -1
- package/dist/commands/validate.js +21 -3
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/whoami.js +16 -6
- package/dist/commands/whoami.js.map +1 -1
- package/dist/context.js +10 -3
- package/dist/context.js.map +1 -1
- package/dist/ui.d.ts +36 -0
- package/dist/ui.js +77 -2
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
- package/templates/package.json.tmpl +1 -1
package/dist/ui.d.ts
CHANGED
|
@@ -31,9 +31,27 @@ export interface Glyphs {
|
|
|
31
31
|
err: string;
|
|
32
32
|
warn: string;
|
|
33
33
|
info: string;
|
|
34
|
+
/** The result-block title marker — the same `◇` the clack wizard draws. */
|
|
35
|
+
step: string;
|
|
34
36
|
}
|
|
35
37
|
/** One row of `ui.keyValue`. */
|
|
36
38
|
export type KeyValueRow = readonly [key: string, value: string];
|
|
39
|
+
/** One row of `ui.result`: a dim label and a value, or several values stacked under one label. */
|
|
40
|
+
export type ResultRow = readonly [
|
|
41
|
+
label: string,
|
|
42
|
+
value: string | readonly string[]
|
|
43
|
+
];
|
|
44
|
+
export type ResultTone = 'ok' | 'warn' | 'err';
|
|
45
|
+
/** What every command's successful human-mode ending is built from. */
|
|
46
|
+
export interface ResultBlock {
|
|
47
|
+
/** The one-line outcome: "Signed in", "Submitted", … Title case, no verb prefix, no period. */
|
|
48
|
+
title: string;
|
|
49
|
+
/** Defaults to `ok` (green marker). `warn` for a result that succeeded but needs attention. */
|
|
50
|
+
tone?: ResultTone;
|
|
51
|
+
rows?: readonly ResultRow[];
|
|
52
|
+
/** Secondary prose, dim, set apart from the rows by a blank line. Keep it short. */
|
|
53
|
+
notes?: readonly string[];
|
|
54
|
+
}
|
|
37
55
|
export interface CodeframeOptions {
|
|
38
56
|
/** Not rendered directly — callers already print `file:line` above/around this. Kept for future use and self-documentation at call sites. */
|
|
39
57
|
file?: string;
|
|
@@ -58,6 +76,13 @@ export interface Ui {
|
|
|
58
76
|
section(title: string): string;
|
|
59
77
|
/** Aligned `key value` rows, 2-space gutter, keys dimmed. */
|
|
60
78
|
keyValue(rows: readonly KeyValueRow[]): string;
|
|
79
|
+
/**
|
|
80
|
+
* THE success renderer: a marker + bold title line, then aligned
|
|
81
|
+
* `label value` rows indented under it, labels dim. A multi-value row
|
|
82
|
+
* stacks its values under one label. Plain strings throughout, so a
|
|
83
|
+
* monochrome terminal gets exactly the same layout minus the color.
|
|
84
|
+
*/
|
|
85
|
+
result(block: ResultBlock): string;
|
|
61
86
|
/** A header-having, left/right-aligned table with no box-drawing. */
|
|
62
87
|
table(headers: readonly string[], rows: readonly (readonly (string | number)[])[]): string;
|
|
63
88
|
/** A `→ ` prefixed, accent-colored hint line. */
|
|
@@ -73,3 +98,14 @@ export interface Ui {
|
|
|
73
98
|
badge(text: string, kind?: BadgeKind): string;
|
|
74
99
|
}
|
|
75
100
|
export declare function createUi(probe: ColorProbe): Ui;
|
|
101
|
+
/**
|
|
102
|
+
* A host URL as a person would say it: `https://x.site` -> `x.site`. The
|
|
103
|
+
* scheme only stays when it carries information (plain `http://`, as in a
|
|
104
|
+
* local sandbox) or when the string is not a URL at all.
|
|
105
|
+
*/
|
|
106
|
+
export declare function shortHost(host: string): string;
|
|
107
|
+
/** A path relative to `cwd` when it sits inside it, `~/…` under home, else as given. */
|
|
108
|
+
export declare function displayPath(target: string, cwd: string, home?: string | undefined): string;
|
|
109
|
+
/** `412ms` below a second, `1.2s` above, `2m 05s` past a minute. */
|
|
110
|
+
export declare function formatDuration(ms: number): string;
|
|
111
|
+
export declare function pluralize(count: number, noun: string): string;
|
package/dist/ui.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
1
2
|
import pc from 'picocolors';
|
|
2
3
|
/**
|
|
3
4
|
* The one color-on/off decision for the whole CLI. True only when the
|
|
@@ -33,8 +34,20 @@ function unicodeSupported(env) {
|
|
|
33
34
|
return false;
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
36
|
-
const UNICODE_GLYPHS = {
|
|
37
|
-
|
|
37
|
+
const UNICODE_GLYPHS = {
|
|
38
|
+
ok: '✓',
|
|
39
|
+
err: '✖',
|
|
40
|
+
warn: '▲',
|
|
41
|
+
info: '●',
|
|
42
|
+
step: '◇',
|
|
43
|
+
};
|
|
44
|
+
const ASCII_GLYPHS = {
|
|
45
|
+
ok: 'ok',
|
|
46
|
+
err: 'x',
|
|
47
|
+
warn: '!',
|
|
48
|
+
info: '*',
|
|
49
|
+
step: 'o',
|
|
50
|
+
};
|
|
38
51
|
/** Right-pad (left-align) a cell to `width`, ignoring color codes in length math is the CALLER's job — pad before coloring, never after. */
|
|
39
52
|
function padCell(text, width, align) {
|
|
40
53
|
return align === 'right' ? text.padStart(width) : text.padEnd(width);
|
|
@@ -67,6 +80,24 @@ export function createUi(probe) {
|
|
|
67
80
|
.map(([key, value]) => `${dim(key.padEnd(keyWidth))} ${value}`)
|
|
68
81
|
.join('\n');
|
|
69
82
|
},
|
|
83
|
+
result(block) {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
const paint = { ok, warn, err }[(_a = block.tone) !== null && _a !== void 0 ? _a : 'ok'];
|
|
86
|
+
const lines = [`${paint(glyphs.step)} ${bold(block.title)}`];
|
|
87
|
+
const rows = (_b = block.rows) !== null && _b !== void 0 ? _b : [];
|
|
88
|
+
const labelWidth = Math.max(0, ...rows.map(([label]) => label.length));
|
|
89
|
+
for (const [label, value] of rows) {
|
|
90
|
+
const values = typeof value === 'string' ? [value] : value;
|
|
91
|
+
values.forEach((line, index) => {
|
|
92
|
+
const gutter = (index === 0 ? label : '').padEnd(labelWidth);
|
|
93
|
+
lines.push(` ${dim(gutter)} ${line}`.trimEnd());
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (block.notes && block.notes.length > 0) {
|
|
97
|
+
lines.push('', ...block.notes.map((note) => ` ${dim(note)}`));
|
|
98
|
+
}
|
|
99
|
+
return lines.join('\n');
|
|
100
|
+
},
|
|
70
101
|
table(headers, rows) {
|
|
71
102
|
const columnCount = headers.length;
|
|
72
103
|
const isNumeric = (value) => typeof value === 'number' ||
|
|
@@ -123,4 +154,48 @@ export function createUi(probe) {
|
|
|
123
154
|
},
|
|
124
155
|
};
|
|
125
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* A host URL as a person would say it: `https://x.site` -> `x.site`. The
|
|
159
|
+
* scheme only stays when it carries information (plain `http://`, as in a
|
|
160
|
+
* local sandbox) or when the string is not a URL at all.
|
|
161
|
+
*/
|
|
162
|
+
export function shortHost(host) {
|
|
163
|
+
let url;
|
|
164
|
+
try {
|
|
165
|
+
url = new URL(host);
|
|
166
|
+
}
|
|
167
|
+
catch (_a) {
|
|
168
|
+
return host;
|
|
169
|
+
}
|
|
170
|
+
const bare = url.pathname === '/' && !url.search && !url.hash;
|
|
171
|
+
return url.protocol === 'https:' && bare ? url.host : host;
|
|
172
|
+
}
|
|
173
|
+
/** A path relative to `cwd` when it sits inside it, `~/…` under home, else as given. */
|
|
174
|
+
export function displayPath(target, cwd, home = process.env.HOME) {
|
|
175
|
+
const relative = path.relative(cwd, target);
|
|
176
|
+
if (relative === '')
|
|
177
|
+
return '.';
|
|
178
|
+
if (!relative.startsWith('..') && !path.isAbsolute(relative))
|
|
179
|
+
return relative;
|
|
180
|
+
if (home) {
|
|
181
|
+
const underHome = path.relative(home, target);
|
|
182
|
+
if (!underHome.startsWith('..') && !path.isAbsolute(underHome)) {
|
|
183
|
+
return `~/${underHome}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return target;
|
|
187
|
+
}
|
|
188
|
+
/** `412ms` below a second, `1.2s` above, `2m 05s` past a minute. */
|
|
189
|
+
export function formatDuration(ms) {
|
|
190
|
+
if (ms < 1000)
|
|
191
|
+
return `${Math.round(ms)}ms`;
|
|
192
|
+
if (ms < 60000)
|
|
193
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
194
|
+
const minutes = Math.floor(ms / 60000);
|
|
195
|
+
const seconds = Math.round((ms % 60000) / 1000);
|
|
196
|
+
return `${minutes}m ${String(seconds).padStart(2, '0')}s`;
|
|
197
|
+
}
|
|
198
|
+
export function pluralize(count, noun) {
|
|
199
|
+
return `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
200
|
+
}
|
|
126
201
|
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import pc from 'picocolors';\n\n/**\n * The CLI's sole style layer. Every color, glyph, and human-output component\n * lives here — nothing else in this package may `import 'picocolors'` or\n * write a raw ANSI escape (the gradient banner in `banner.ts` is the one\n * documented exception: it paints a true-color gradient picocolors cannot\n * express, but even it asks THIS module's `colorsEnabled` for the on/off\n * decision, so there is exactly one gate).\n *\n * Everything here renders plain strings. Printing is still `CliContext`'s\n * job (`ctx.log`) — this module never touches a stream directly, so the\n * stdout-purity invariant in `context.ts` cannot be bypassed by a stray\n * color call.\n */\n\n/** What `colorsEnabled` needs to know to decide — never the real stream. */\nexport interface ColorProbe {\n /** Whether the destination (stderr, always — see `context.ts`) is a TTY. */\n isTTY: boolean;\n env: Readonly<Record<string, string | undefined>>;\n}\n\n/**\n * The one color-on/off decision for the whole CLI. True only when the\n * stream is a real terminal AND `NO_COLOR` is unset AND `TERM` is not\n * `\"dumb\"` — EXCEPT `FORCE_COLOR`, which overrides everything else on when\n * set to anything other than `\"0\"` (matching the de facto convention: Node,\n * chalk, and picocolors itself all treat a defined-and-nonzero `FORCE_COLOR`\n * as \"yes, even if you don't think you can\").\n */\nexport function colorsEnabled(probe: ColorProbe): boolean {\n const { env } = probe;\n\n if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== '0') return true;\n if (env.NO_COLOR !== undefined) return false;\n if (env.TERM === 'dumb') return false;\n\n return probe.isTTY;\n}\n\n/**\n * Whether the destination can render the box-drawing/Unicode glyphs this\n * module prefers. Deliberately simple: a \"dumb\" terminal never can, and a\n * locale that names a non-UTF-8 charset can't be trusted to either. Anything\n * else — including an unset locale, which is the common case for CI/agent\n * environments — is assumed capable rather than guessed at further.\n */\nfunction unicodeSupported(\n env: Readonly<Record<string, string | undefined>>,\n): boolean {\n if (env.TERM === 'dumb') return false;\n\n const locale = env.LANG ?? env.LC_ALL ?? env.LC_CTYPE ?? '';\n if (locale && !/utf-?8/i.test(locale)) return false;\n\n return true;\n}\n\nexport interface Glyphs {\n ok: string;\n err: string;\n warn: string;\n info: string;\n}\n\nconst UNICODE_GLYPHS: Glyphs = { ok: '✓', err: '✖', warn: '▲', info: '●' };\nconst ASCII_GLYPHS: Glyphs = { ok: 'ok', err: 'x', warn: '!', info: '*' };\n\n/** One row of `ui.keyValue`. */\nexport type KeyValueRow = readonly [key: string, value: string];\n\nexport interface CodeframeOptions {\n /** Not rendered directly — callers already print `file:line` above/around this. Kept for future use and self-documentation at call sites. */\n file?: string;\n /** 1-indexed line to highlight. */\n line: number;\n /** The full source text of `file`. */\n source: string;\n /** Lines of context above/below the target line. Defaults to 2. */\n contextLines?: number;\n}\n\nexport type BadgeKind = 'dim' | 'accent' | 'warn' | 'err';\n\nexport interface Ui {\n readonly colors: boolean;\n readonly glyphs: Glyphs;\n\n err(text: string): string;\n warn(text: string): string;\n ok(text: string): string;\n accent(text: string): string;\n dim(text: string): string;\n bold(text: string): string;\n\n /** A dim, uppercased section header. */\n section(title: string): string;\n /** Aligned `key value` rows, 2-space gutter, keys dimmed. */\n keyValue(rows: readonly KeyValueRow[]): string;\n /** A header-having, left/right-aligned table with no box-drawing. */\n table(\n headers: readonly string[],\n rows: readonly (readonly (string | number)[])[],\n ): string;\n /** A `→ ` prefixed, accent-colored hint line. */\n hint(text: string): string;\n /**\n * THE closing line of any failure path: a blank line, then a dim\n * `→ <why>: ` (or bare `→ `) followed by the bold remediation command.\n */\n nextAction(cmd: string, why?: string): string;\n /** A Prisma/Vite-style source snippet around one line. */\n codeframe(opts: CodeframeOptions): string;\n /** A bracketed, dim-by-default tag: `[gate · 2h ago]`. */\n badge(text: string, kind?: BadgeKind): string;\n}\n\n/** Right-pad (left-align) a cell to `width`, ignoring color codes in length math is the CALLER's job — pad before coloring, never after. */\nfunction padCell(text: string, width: number, align: 'left' | 'right'): string {\n return align === 'right' ? text.padStart(width) : text.padEnd(width);\n}\n\nexport function createUi(probe: ColorProbe): Ui {\n const colors = colorsEnabled(probe);\n const c = pc.createColors(colors);\n const glyphs = unicodeSupported(probe.env) ? UNICODE_GLYPHS : ASCII_GLYPHS;\n\n const err = (text: string) => c.red(text);\n const warn = (text: string) => c.yellow(text);\n const ok = (text: string) => c.green(text);\n const accent = (text: string) => c.cyan(text);\n const dim = (text: string) => c.dim(text);\n const bold = (text: string) => c.bold(text);\n\n return {\n colors,\n glyphs,\n err,\n warn,\n ok,\n accent,\n dim,\n bold,\n\n section(title: string): string {\n return dim(title.toUpperCase());\n },\n\n keyValue(rows: readonly KeyValueRow[]): string {\n const keyWidth = Math.max(0, ...rows.map(([key]) => key.length));\n\n return rows\n .map(([key, value]) => `${dim(key.padEnd(keyWidth))} ${value}`)\n .join('\\n');\n },\n\n table(\n headers: readonly string[],\n rows: readonly (readonly (string | number)[])[],\n ): string {\n const columnCount = headers.length;\n const isNumeric = (value: string | number) =>\n typeof value === 'number' ||\n (value.trim() !== '' && !Number.isNaN(Number(value)));\n\n const aligns: Array<'left' | 'right'> = Array.from(\n { length: columnCount },\n (_, col) =>\n rows.length > 0 && rows.every((row) => isNumeric(row[col] ?? ''))\n ? 'right'\n : 'left',\n );\n\n const widths = Array.from({ length: columnCount }, (_, col) =>\n Math.max(\n headers[col]?.length ?? 0,\n ...rows.map((row) => String(row[col] ?? '').length),\n ),\n );\n\n const headerLine = headers\n .map((header, col) =>\n dim(\n padCell(\n header.toUpperCase(),\n widths[col] ?? 0,\n aligns[col] ?? 'left',\n ),\n ),\n )\n .join(' ');\n\n const bodyLines = rows.map((row) =>\n row\n .map((cell, col) =>\n padCell(String(cell), widths[col] ?? 0, aligns[col] ?? 'left'),\n )\n .join(' '),\n );\n\n return [headerLine, ...bodyLines].join('\\n');\n },\n\n hint(text: string): string {\n return accent(`→ ${text}`);\n },\n\n nextAction(cmd: string, why?: string): string {\n const prefix = why ? `→ ${why}: ` : '→ ';\n return `\\n${dim(prefix)}${bold(cmd)}`;\n },\n\n codeframe(opts: CodeframeOptions): string {\n const contextLines = opts.contextLines ?? 2;\n const lines = opts.source.split('\\n');\n const start = Math.max(1, opts.line - contextLines);\n const end = Math.min(lines.length, opts.line + contextLines);\n const gutterWidth = String(end).length;\n\n const rows: string[] = [];\n for (let n = start; n <= end; n += 1) {\n const isTarget = n === opts.line;\n const text = lines[n - 1] ?? '';\n const marker = isTarget ? '>' : ' ';\n const lineNo = String(n).padStart(gutterWidth);\n const rendered = isTarget ? bold(text) : text;\n\n rows.push(\n `${isTarget ? accent(marker) : marker} ${dim(lineNo)} ${dim('│')} ${rendered}`,\n );\n }\n\n return rows.join('\\n');\n },\n\n badge(text: string, kind: BadgeKind = 'dim'): string {\n const wrapped = `[${text}]`;\n\n switch (kind) {\n case 'err':\n return err(wrapped);\n case 'warn':\n return warn(wrapped);\n case 'accent':\n return accent(wrapped);\n default:\n return dim(wrapped);\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport pc from 'picocolors';\n\n/**\n * The CLI's sole style layer. Every color, glyph, and human-output component\n * lives here — nothing else in this package may `import 'picocolors'` or\n * write a raw ANSI escape (the gradient banner in `banner.ts` is the one\n * documented exception: it paints a true-color gradient picocolors cannot\n * express, but even it asks THIS module's `colorsEnabled` for the on/off\n * decision, so there is exactly one gate).\n *\n * Everything here renders plain strings. Printing is still `CliContext`'s\n * job (`ctx.log`) — this module never touches a stream directly, so the\n * stdout-purity invariant in `context.ts` cannot be bypassed by a stray\n * color call.\n */\n\n/** What `colorsEnabled` needs to know to decide — never the real stream. */\nexport interface ColorProbe {\n /** Whether the destination (stderr, always — see `context.ts`) is a TTY. */\n isTTY: boolean;\n env: Readonly<Record<string, string | undefined>>;\n}\n\n/**\n * The one color-on/off decision for the whole CLI. True only when the\n * stream is a real terminal AND `NO_COLOR` is unset AND `TERM` is not\n * `\"dumb\"` — EXCEPT `FORCE_COLOR`, which overrides everything else on when\n * set to anything other than `\"0\"` (matching the de facto convention: Node,\n * chalk, and picocolors itself all treat a defined-and-nonzero `FORCE_COLOR`\n * as \"yes, even if you don't think you can\").\n */\nexport function colorsEnabled(probe: ColorProbe): boolean {\n const { env } = probe;\n\n if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== '0') return true;\n if (env.NO_COLOR !== undefined) return false;\n if (env.TERM === 'dumb') return false;\n\n return probe.isTTY;\n}\n\n/**\n * Whether the destination can render the box-drawing/Unicode glyphs this\n * module prefers. Deliberately simple: a \"dumb\" terminal never can, and a\n * locale that names a non-UTF-8 charset can't be trusted to either. Anything\n * else — including an unset locale, which is the common case for CI/agent\n * environments — is assumed capable rather than guessed at further.\n */\nfunction unicodeSupported(\n env: Readonly<Record<string, string | undefined>>,\n): boolean {\n if (env.TERM === 'dumb') return false;\n\n const locale = env.LANG ?? env.LC_ALL ?? env.LC_CTYPE ?? '';\n if (locale && !/utf-?8/i.test(locale)) return false;\n\n return true;\n}\n\nexport interface Glyphs {\n ok: string;\n err: string;\n warn: string;\n info: string;\n /** The result-block title marker — the same `◇` the clack wizard draws. */\n step: string;\n}\n\nconst UNICODE_GLYPHS: Glyphs = {\n ok: '✓',\n err: '✖',\n warn: '▲',\n info: '●',\n step: '◇',\n};\nconst ASCII_GLYPHS: Glyphs = {\n ok: 'ok',\n err: 'x',\n warn: '!',\n info: '*',\n step: 'o',\n};\n\n/** One row of `ui.keyValue`. */\nexport type KeyValueRow = readonly [key: string, value: string];\n\n/** One row of `ui.result`: a dim label and a value, or several values stacked under one label. */\nexport type ResultRow = readonly [\n label: string,\n value: string | readonly string[],\n];\n\nexport type ResultTone = 'ok' | 'warn' | 'err';\n\n/** What every command's successful human-mode ending is built from. */\nexport interface ResultBlock {\n /** The one-line outcome: \"Signed in\", \"Submitted\", … Title case, no verb prefix, no period. */\n title: string;\n /** Defaults to `ok` (green marker). `warn` for a result that succeeded but needs attention. */\n tone?: ResultTone;\n rows?: readonly ResultRow[];\n /** Secondary prose, dim, set apart from the rows by a blank line. Keep it short. */\n notes?: readonly string[];\n}\n\nexport interface CodeframeOptions {\n /** Not rendered directly — callers already print `file:line` above/around this. Kept for future use and self-documentation at call sites. */\n file?: string;\n /** 1-indexed line to highlight. */\n line: number;\n /** The full source text of `file`. */\n source: string;\n /** Lines of context above/below the target line. Defaults to 2. */\n contextLines?: number;\n}\n\nexport type BadgeKind = 'dim' | 'accent' | 'warn' | 'err';\n\nexport interface Ui {\n readonly colors: boolean;\n readonly glyphs: Glyphs;\n\n err(text: string): string;\n warn(text: string): string;\n ok(text: string): string;\n accent(text: string): string;\n dim(text: string): string;\n bold(text: string): string;\n\n /** A dim, uppercased section header. */\n section(title: string): string;\n /** Aligned `key value` rows, 2-space gutter, keys dimmed. */\n keyValue(rows: readonly KeyValueRow[]): string;\n /**\n * THE success renderer: a marker + bold title line, then aligned\n * `label value` rows indented under it, labels dim. A multi-value row\n * stacks its values under one label. Plain strings throughout, so a\n * monochrome terminal gets exactly the same layout minus the color.\n */\n result(block: ResultBlock): string;\n /** A header-having, left/right-aligned table with no box-drawing. */\n table(\n headers: readonly string[],\n rows: readonly (readonly (string | number)[])[],\n ): string;\n /** A `→ ` prefixed, accent-colored hint line. */\n hint(text: string): string;\n /**\n * THE closing line of any failure path: a blank line, then a dim\n * `→ <why>: ` (or bare `→ `) followed by the bold remediation command.\n */\n nextAction(cmd: string, why?: string): string;\n /** A Prisma/Vite-style source snippet around one line. */\n codeframe(opts: CodeframeOptions): string;\n /** A bracketed, dim-by-default tag: `[gate · 2h ago]`. */\n badge(text: string, kind?: BadgeKind): string;\n}\n\n/** Right-pad (left-align) a cell to `width`, ignoring color codes in length math is the CALLER's job — pad before coloring, never after. */\nfunction padCell(text: string, width: number, align: 'left' | 'right'): string {\n return align === 'right' ? text.padStart(width) : text.padEnd(width);\n}\n\nexport function createUi(probe: ColorProbe): Ui {\n const colors = colorsEnabled(probe);\n const c = pc.createColors(colors);\n const glyphs = unicodeSupported(probe.env) ? UNICODE_GLYPHS : ASCII_GLYPHS;\n\n const err = (text: string) => c.red(text);\n const warn = (text: string) => c.yellow(text);\n const ok = (text: string) => c.green(text);\n const accent = (text: string) => c.cyan(text);\n const dim = (text: string) => c.dim(text);\n const bold = (text: string) => c.bold(text);\n\n return {\n colors,\n glyphs,\n err,\n warn,\n ok,\n accent,\n dim,\n bold,\n\n section(title: string): string {\n return dim(title.toUpperCase());\n },\n\n keyValue(rows: readonly KeyValueRow[]): string {\n const keyWidth = Math.max(0, ...rows.map(([key]) => key.length));\n\n return rows\n .map(([key, value]) => `${dim(key.padEnd(keyWidth))} ${value}`)\n .join('\\n');\n },\n\n result(block: ResultBlock): string {\n const paint = { ok, warn, err }[block.tone ?? 'ok'];\n const lines = [`${paint(glyphs.step)} ${bold(block.title)}`];\n const rows = block.rows ?? [];\n const labelWidth = Math.max(0, ...rows.map(([label]) => label.length));\n\n for (const [label, value] of rows) {\n const values = typeof value === 'string' ? [value] : value;\n values.forEach((line, index) => {\n const gutter = (index === 0 ? label : '').padEnd(labelWidth);\n lines.push(` ${dim(gutter)} ${line}`.trimEnd());\n });\n }\n\n if (block.notes && block.notes.length > 0) {\n lines.push('', ...block.notes.map((note) => ` ${dim(note)}`));\n }\n\n return lines.join('\\n');\n },\n\n table(\n headers: readonly string[],\n rows: readonly (readonly (string | number)[])[],\n ): string {\n const columnCount = headers.length;\n const isNumeric = (value: string | number) =>\n typeof value === 'number' ||\n (value.trim() !== '' && !Number.isNaN(Number(value)));\n\n const aligns: Array<'left' | 'right'> = Array.from(\n { length: columnCount },\n (_, col) =>\n rows.length > 0 && rows.every((row) => isNumeric(row[col] ?? ''))\n ? 'right'\n : 'left',\n );\n\n const widths = Array.from({ length: columnCount }, (_, col) =>\n Math.max(\n headers[col]?.length ?? 0,\n ...rows.map((row) => String(row[col] ?? '').length),\n ),\n );\n\n const headerLine = headers\n .map((header, col) =>\n dim(\n padCell(\n header.toUpperCase(),\n widths[col] ?? 0,\n aligns[col] ?? 'left',\n ),\n ),\n )\n .join(' ');\n\n const bodyLines = rows.map((row) =>\n row\n .map((cell, col) =>\n padCell(String(cell), widths[col] ?? 0, aligns[col] ?? 'left'),\n )\n .join(' '),\n );\n\n return [headerLine, ...bodyLines].join('\\n');\n },\n\n hint(text: string): string {\n return accent(`→ ${text}`);\n },\n\n nextAction(cmd: string, why?: string): string {\n const prefix = why ? `→ ${why}: ` : '→ ';\n return `\\n${dim(prefix)}${bold(cmd)}`;\n },\n\n codeframe(opts: CodeframeOptions): string {\n const contextLines = opts.contextLines ?? 2;\n const lines = opts.source.split('\\n');\n const start = Math.max(1, opts.line - contextLines);\n const end = Math.min(lines.length, opts.line + contextLines);\n const gutterWidth = String(end).length;\n\n const rows: string[] = [];\n for (let n = start; n <= end; n += 1) {\n const isTarget = n === opts.line;\n const text = lines[n - 1] ?? '';\n const marker = isTarget ? '>' : ' ';\n const lineNo = String(n).padStart(gutterWidth);\n const rendered = isTarget ? bold(text) : text;\n\n rows.push(\n `${isTarget ? accent(marker) : marker} ${dim(lineNo)} ${dim('│')} ${rendered}`,\n );\n }\n\n return rows.join('\\n');\n },\n\n badge(text: string, kind: BadgeKind = 'dim'): string {\n const wrapped = `[${text}]`;\n\n switch (kind) {\n case 'err':\n return err(wrapped);\n case 'warn':\n return warn(wrapped);\n case 'accent':\n return accent(wrapped);\n default:\n return dim(wrapped);\n }\n },\n };\n}\n\n/**\n * A host URL as a person would say it: `https://x.site` -> `x.site`. The\n * scheme only stays when it carries information (plain `http://`, as in a\n * local sandbox) or when the string is not a URL at all.\n */\nexport function shortHost(host: string): string {\n let url: URL;\n try {\n url = new URL(host);\n } catch {\n return host;\n }\n\n const bare = url.pathname === '/' && !url.search && !url.hash;\n return url.protocol === 'https:' && bare ? url.host : host;\n}\n\n/** A path relative to `cwd` when it sits inside it, `~/…` under home, else as given. */\nexport function displayPath(\n target: string,\n cwd: string,\n home: string | undefined = process.env.HOME,\n): string {\n const relative = path.relative(cwd, target);\n if (relative === '') return '.';\n if (!relative.startsWith('..') && !path.isAbsolute(relative)) return relative;\n\n if (home) {\n const underHome = path.relative(home, target);\n if (!underHome.startsWith('..') && !path.isAbsolute(underHome)) {\n return `~/${underHome}`;\n }\n }\n\n return target;\n}\n\n/** `412ms` below a second, `1.2s` above, `2m 05s` past a minute. */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n\n const minutes = Math.floor(ms / 60_000);\n const seconds = Math.round((ms % 60_000) / 1000);\n return `${minutes}m ${String(seconds).padStart(2, '0')}s`;\n}\n\nexport function pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekanos/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "The Ekanos partner toolchain CLI: scaffold, validate, and test an Ekanos integration against the published @ekanos packages. Agent-native — every verb speaks JSON with a stable exit-code taxonomy.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|