@obov/mext 0.0.0-stage → 0.1.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/dist/build-cli.d.ts +39 -0
- package/dist/build-cli.js +160 -0
- package/dist/build-cli.js.map +1 -0
- package/dist/build.d.ts +65 -0
- package/dist/build.js +252 -0
- package/dist/build.js.map +1 -0
- package/dist/cli/args.d.ts +107 -0
- package/dist/cli/args.js +621 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +13 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/build.d.ts +11 -0
- package/dist/cli/build.js +87 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev-lock.d.ts +23 -0
- package/dist/cli/dev-lock.js +68 -0
- package/dist/cli/dev-lock.js.map +1 -0
- package/dist/cli/dev-server.d.ts +20 -0
- package/dist/cli/dev-server.js +74 -0
- package/dist/cli/dev-server.js.map +1 -0
- package/dist/cli/dev.d.ts +21 -0
- package/dist/cli/dev.js +564 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/help.d.ts +31 -0
- package/dist/cli/help.js +238 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/io.d.ts +26 -0
- package/dist/cli/io.js +74 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/login.d.ts +32 -0
- package/dist/cli/login.js +284 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/logout.d.ts +9 -0
- package/dist/cli/logout.js +105 -0
- package/dist/cli/logout.js.map +1 -0
- package/dist/cli/main.d.ts +19 -0
- package/dist/cli/main.js +77 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/mcp.d.ts +16 -0
- package/dist/cli/mcp.js +92 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/output.d.ts +34 -0
- package/dist/cli/output.js +172 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/remote.d.ts +17 -0
- package/dist/cli/remote.js +35 -0
- package/dist/cli/remote.js.map +1 -0
- package/dist/cli/run.d.ts +14 -0
- package/dist/cli/run.js +218 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/start.d.ts +8 -0
- package/dist/cli/start.js +37 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/client/cache.d.ts +104 -0
- package/dist/client/cache.js +208 -0
- package/dist/client/cache.js.map +1 -0
- package/dist/client/main.d.ts +25 -0
- package/dist/client/main.js +100 -0
- package/dist/client/main.js.map +1 -0
- package/dist/client/manifest.d.ts +19 -0
- package/dist/client/manifest.js +68 -0
- package/dist/client/manifest.js.map +1 -0
- package/dist/client/runner.d.ts +49 -0
- package/dist/client/runner.js +562 -0
- package/dist/client/runner.js.map +1 -0
- package/dist/command.d.ts +75 -0
- package/dist/command.js +44 -0
- package/dist/command.js.map +1 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.js +122 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.js +12 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.d.ts +97 -0
- package/dist/instrumentation.js +165 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/invoke.d.ts +64 -0
- package/dist/invoke.js +152 -0
- package/dist/invoke.js.map +1 -0
- package/dist/lookup.d.ts +9 -0
- package/dist/lookup.js +27 -0
- package/dist/lookup.js.map +1 -0
- package/dist/manifest.d.ts +68 -0
- package/dist/manifest.js +77 -0
- package/dist/manifest.js.map +1 -0
- package/dist/mcp/constants.d.ts +9 -0
- package/dist/mcp/constants.js +10 -0
- package/dist/mcp/constants.js.map +1 -0
- package/dist/mcp/dispatch.d.ts +66 -0
- package/dist/mcp/dispatch.js +249 -0
- package/dist/mcp/dispatch.js.map +1 -0
- package/dist/mcp/help.d.ts +57 -0
- package/dist/mcp/help.js +136 -0
- package/dist/mcp/help.js.map +1 -0
- package/dist/mcp/http.d.ts +92 -0
- package/dist/mcp/http.js +366 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/install.d.ts +17 -0
- package/dist/mcp/install.js +36 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/mrtr.d.ts +82 -0
- package/dist/mcp/mrtr.js +130 -0
- package/dist/mcp/mrtr.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.js +204 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +57 -0
- package/dist/mcp/stdio.js +79 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/principal.d.ts +16 -0
- package/dist/principal.js +24 -0
- package/dist/principal.js.map +1 -0
- package/dist/registry.d.ts +90 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/remote/browser.d.ts +15 -0
- package/dist/remote/browser.js +47 -0
- package/dist/remote/browser.js.map +1 -0
- package/dist/remote/client.d.ts +113 -0
- package/dist/remote/client.js +312 -0
- package/dist/remote/client.js.map +1 -0
- package/dist/remote/credentials.d.ts +48 -0
- package/dist/remote/credentials.js +196 -0
- package/dist/remote/credentials.js.map +1 -0
- package/dist/remote/index.d.ts +8 -0
- package/dist/remote/index.js +5 -0
- package/dist/remote/index.js.map +1 -0
- package/dist/remote/oauth.d.ts +186 -0
- package/dist/remote/oauth.js +507 -0
- package/dist/remote/oauth.js.map +1 -0
- package/dist/scan.d.ts +34 -0
- package/dist/scan.js +155 -0
- package/dist/scan.js.map +1 -0
- package/dist/start.d.ts +63 -0
- package/dist/start.js +267 -0
- package/dist/start.js.map +1 -0
- package/dist/suggest.d.ts +7 -0
- package/dist/suggest.js +45 -0
- package/dist/suggest.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -4
- package/README.md +0 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NamespaceInfo, RegisteredCommand, Registry } from "../registry.ts";
|
|
2
|
+
/**
|
|
3
|
+
* help 생성 (D2). 일반 텍스트, ANSI 색 없음, 같은 Registry 에 같은 출력.
|
|
4
|
+
* 대상 = "" (루트) · 네임스페이스 ID · 명령어 ID (별칭 포함). prog = 명령어 앞 접두 ("mext run" · "myapp").
|
|
5
|
+
*/
|
|
6
|
+
export interface HelpOptions {
|
|
7
|
+
/** 명령어 경로 앞에 붙는 실행 접두. 예: "mext run" */
|
|
8
|
+
prog: string;
|
|
9
|
+
}
|
|
10
|
+
export type HelpTarget = {
|
|
11
|
+
kind: "namespace";
|
|
12
|
+
namespace: NamespaceInfo;
|
|
13
|
+
command: RegisteredCommand | undefined;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "command";
|
|
16
|
+
command: RegisteredCommand;
|
|
17
|
+
};
|
|
18
|
+
/** 대상 해석. 네임스페이스가 우선 (users = 네임스페이스 + index.ts 명령어면 둘 다). 없으면 UNKNOWN_COMMAND */
|
|
19
|
+
export declare function resolveHelpTarget(registry: Registry, target: string): HelpTarget;
|
|
20
|
+
/** 대상의 help 텍스트 (끝 줄바꿈 포함) */
|
|
21
|
+
export declare function renderHelp(registry: Registry, target: string, options: HelpOptions): string;
|
|
22
|
+
export declare function renderNamespaceHelp(registry: Registry, ns: NamespaceInfo, options: HelpOptions): string;
|
|
23
|
+
export declare function renderCommandHelp(entry: RegisteredCommand, options: HelpOptions): string;
|
|
24
|
+
/** 입력 객체 → CLI 호출 문자열 (positional · 점 플래그 · 반복 · JSON 문자열) */
|
|
25
|
+
export declare function formatInvocation(entry: RegisteredCommand, input: unknown, options: HelpOptions): string;
|
|
26
|
+
/** 명령어 · 네임스페이스 ID → CLI 경로 ("users.get" → "users get") */
|
|
27
|
+
export declare function cliPath(id: string): string;
|
|
28
|
+
/** 두 칸 간격 정렬 표. 끝 공백 제거 */
|
|
29
|
+
export declare function table(rows: readonly (readonly string[])[]): string[];
|
|
30
|
+
/** 셸에 그대로 붙여 넣을 수 있는 인용 */
|
|
31
|
+
export declare function shellQuote(value: string): string;
|
package/dist/cli/help.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { findCommand } from "../lookup.js";
|
|
2
|
+
import { cliSpec } from "./args.js";
|
|
3
|
+
/** 대상 해석. 네임스페이스가 우선 (users = 네임스페이스 + index.ts 명령어면 둘 다). 없으면 UNKNOWN_COMMAND */
|
|
4
|
+
export function resolveHelpTarget(registry, target) {
|
|
5
|
+
const ns = registry.namespaces.get(target);
|
|
6
|
+
if (ns)
|
|
7
|
+
return { kind: "namespace", namespace: ns, command: target === "" ? undefined : registry.commands.get(target) };
|
|
8
|
+
return { kind: "command", command: findCommand(registry, target) };
|
|
9
|
+
}
|
|
10
|
+
/** 대상의 help 텍스트 (끝 줄바꿈 포함) */
|
|
11
|
+
export function renderHelp(registry, target, options) {
|
|
12
|
+
const t = resolveHelpTarget(registry, target);
|
|
13
|
+
if (t.kind === "command")
|
|
14
|
+
return renderCommandHelp(t.command, options);
|
|
15
|
+
const text = renderNamespaceHelp(registry, t.namespace, options);
|
|
16
|
+
return t.command ? `${text}\n${renderCommandHelp(t.command, options)}` : text;
|
|
17
|
+
}
|
|
18
|
+
export function renderNamespaceHelp(registry, ns, options) {
|
|
19
|
+
const out = [];
|
|
20
|
+
const path = cliPath(ns.id);
|
|
21
|
+
const prefix = path === "" ? options.prog : `${options.prog} ${path}`;
|
|
22
|
+
if (ns.id !== "")
|
|
23
|
+
out.push(`Namespace: ${path}`, "");
|
|
24
|
+
if (ns.description)
|
|
25
|
+
out.push(ns.description, "");
|
|
26
|
+
out.push("Usage:");
|
|
27
|
+
out.push(` ${prefix} <command> [args] [flags]`);
|
|
28
|
+
if (ns.id === "" && ns.namespaces.length > 0)
|
|
29
|
+
out.push(` ${prefix} <namespace> --help`);
|
|
30
|
+
out.push(` ${prefix} <command> --help`, "");
|
|
31
|
+
if (ns.namespaces.length > 0) {
|
|
32
|
+
out.push("Namespaces:");
|
|
33
|
+
out.push(...table(ns.namespaces.map((id) => [lastSegment(id), summaryOf(registry.namespaces.get(id)) ?? ""])).map((l) => ` ${l}`), "");
|
|
34
|
+
}
|
|
35
|
+
if (ns.commands.length > 0) {
|
|
36
|
+
out.push("Commands:");
|
|
37
|
+
out.push(...table(ns.commands.map((id) => {
|
|
38
|
+
const c = registry.commands.get(id).command;
|
|
39
|
+
return [lastSegment(id), c.description];
|
|
40
|
+
})).map((l) => ` ${l}`), "");
|
|
41
|
+
}
|
|
42
|
+
out.push("Global flags:", ...globalFlagLines(false), "");
|
|
43
|
+
return finish(out);
|
|
44
|
+
}
|
|
45
|
+
export function renderCommandHelp(entry, options) {
|
|
46
|
+
const c = entry.command;
|
|
47
|
+
const spec = cliSpec(entry);
|
|
48
|
+
const path = cliPath(entry.id);
|
|
49
|
+
const out = [c.description, ""];
|
|
50
|
+
const positional = spec.positional.map((k) => spec.fields.find((f) => f.key === k)).filter(Boolean);
|
|
51
|
+
const usageArgs = positional.map((f) => {
|
|
52
|
+
const name = f.kind.type === "array" ? `${f.flag}...` : f.flag;
|
|
53
|
+
return f.required ? `<${name}>` : `[${name}]`;
|
|
54
|
+
});
|
|
55
|
+
out.push("Usage:", ` ${[options.prog, path, ...usageArgs, "[flags]"].join(" ")}`, "");
|
|
56
|
+
if (positional.length > 0) {
|
|
57
|
+
out.push("Arguments:");
|
|
58
|
+
out.push(...table(positional.map((f) => [`<${f.flag}>`, annotate(f, [`or --${f.flag}`])])).map((l) => ` ${l}`), "");
|
|
59
|
+
}
|
|
60
|
+
const flagRows = [];
|
|
61
|
+
for (const f of spec.fields) {
|
|
62
|
+
if (spec.positional.includes(f.key))
|
|
63
|
+
continue;
|
|
64
|
+
flagRows.push(...fieldRows(f, ""));
|
|
65
|
+
}
|
|
66
|
+
if (flagRows.length > 0)
|
|
67
|
+
out.push("Flags:", ...table(flagRows).map((l) => ` ${l}`), "");
|
|
68
|
+
out.push("Global flags:", ...globalFlagLines(true), "");
|
|
69
|
+
const safety = [c.readOnly ? "read-only" : "writes"];
|
|
70
|
+
if (c.destructive)
|
|
71
|
+
safety.push("destructive");
|
|
72
|
+
if (c.requiresConfirmation)
|
|
73
|
+
safety.push("requires confirmation (--yes to approve)");
|
|
74
|
+
if (c.scopes.length > 0)
|
|
75
|
+
safety.push(`scopes: ${c.scopes.join(", ")}`);
|
|
76
|
+
out.push(`Safety: ${safety.join(", ")}`);
|
|
77
|
+
if (entry.aliases.length > 0)
|
|
78
|
+
out.push(`Aliases: ${entry.aliases.map(cliPath).join(", ")}`);
|
|
79
|
+
out.push("");
|
|
80
|
+
if (c.examples.length > 0) {
|
|
81
|
+
out.push("Examples:", ...c.examples.map((e) => ` ${formatInvocation(entry, e, options)}`), "");
|
|
82
|
+
}
|
|
83
|
+
return finish(out);
|
|
84
|
+
}
|
|
85
|
+
/** 입력 객체 → CLI 호출 문자열 (positional · 점 플래그 · 반복 · JSON 문자열) */
|
|
86
|
+
export function formatInvocation(entry, input, options) {
|
|
87
|
+
const spec = cliSpec(entry);
|
|
88
|
+
const parts = [options.prog, cliPath(entry.id)];
|
|
89
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
90
|
+
return [...parts, "--input-json", shellQuote(JSON.stringify(input))].join(" ");
|
|
91
|
+
}
|
|
92
|
+
const obj = input;
|
|
93
|
+
const flags = [];
|
|
94
|
+
const known = new Set(spec.fields.map((f) => f.key));
|
|
95
|
+
if (Object.keys(obj).some((k) => !known.has(k))) {
|
|
96
|
+
return [...parts, "--input-json", shellQuote(JSON.stringify(input))].join(" ");
|
|
97
|
+
}
|
|
98
|
+
let positionalOpen = true;
|
|
99
|
+
for (const key of spec.positional) {
|
|
100
|
+
const f = spec.fields.find((x) => x.key === key);
|
|
101
|
+
const v = obj[key];
|
|
102
|
+
if (!positionalOpen || v === undefined) {
|
|
103
|
+
positionalOpen = false;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (f.kind.type === "array" && Array.isArray(v) && key === spec.positional.at(-1)) {
|
|
107
|
+
parts.push(...v.map((x) => shellQuote(String(x))));
|
|
108
|
+
}
|
|
109
|
+
else if (isScalarKind(f.kind) && isScalarValue(v)) {
|
|
110
|
+
parts.push(shellQuote(String(v)));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
positionalOpen = false;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
known.delete(key);
|
|
117
|
+
}
|
|
118
|
+
for (const f of spec.fields) {
|
|
119
|
+
if (!known.has(f.key) || obj[f.key] === undefined)
|
|
120
|
+
continue;
|
|
121
|
+
flags.push(...flagTokens(f, `--${f.flag}`, obj[f.key]));
|
|
122
|
+
}
|
|
123
|
+
return [...parts, ...flags].join(" ");
|
|
124
|
+
}
|
|
125
|
+
function flagTokens(f, flag, v) {
|
|
126
|
+
const kind = f.kind;
|
|
127
|
+
if (kind.type === "boolean" && typeof v === "boolean")
|
|
128
|
+
return [v ? flag : `--no-${flag.slice(2)}`];
|
|
129
|
+
if (kind.type === "array" && Array.isArray(v) && v.every(isScalarValue)) {
|
|
130
|
+
return v.flatMap((x) => (kind.item.type === "boolean" ? [`${flag}=${x}`] : [flag, shellQuote(String(x))]));
|
|
131
|
+
}
|
|
132
|
+
if (kind.type === "object" && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
133
|
+
const entries = Object.entries(v);
|
|
134
|
+
const subs = kind.fields;
|
|
135
|
+
if (entries.every(([k]) => subs.some((s) => s.key === k))) {
|
|
136
|
+
return entries
|
|
137
|
+
.filter(([, x]) => x !== undefined)
|
|
138
|
+
.flatMap(([k, x]) => {
|
|
139
|
+
const s = subs.find((y) => y.key === k);
|
|
140
|
+
return flagTokens(s, `${flag}.${s.flag}`, x);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (isScalarKind(kind) && isScalarValue(v))
|
|
145
|
+
return [flag, shellQuote(String(v))];
|
|
146
|
+
return [flag, shellQuote(JSON.stringify(v))];
|
|
147
|
+
}
|
|
148
|
+
function fieldRows(f, parent) {
|
|
149
|
+
const name = parent === "" ? f.flag : `${parent}.${f.flag}`;
|
|
150
|
+
const kind = f.kind;
|
|
151
|
+
if (kind.type === "object") {
|
|
152
|
+
const rows = [[`--${name} <json>`, annotate(f, [])]];
|
|
153
|
+
for (const s of kind.fields)
|
|
154
|
+
rows.push(...fieldRows(s, name));
|
|
155
|
+
return rows;
|
|
156
|
+
}
|
|
157
|
+
if (kind.type === "boolean")
|
|
158
|
+
return [[`--[no-]${name}`, annotate(f, [])]];
|
|
159
|
+
if (kind.type === "array") {
|
|
160
|
+
const left = kind.item.type === "boolean" ? `--${name}[=true|false]` : `--${name} <${metavar(kind.item)}>`;
|
|
161
|
+
return [[left, annotate(f, ["repeatable"])]];
|
|
162
|
+
}
|
|
163
|
+
return [[`--${name} <${metavar(kind)}>`, annotate(f, [])]];
|
|
164
|
+
}
|
|
165
|
+
function metavar(kind) {
|
|
166
|
+
switch (kind.type) {
|
|
167
|
+
case "string":
|
|
168
|
+
return "string";
|
|
169
|
+
case "number":
|
|
170
|
+
return "number";
|
|
171
|
+
case "boolean":
|
|
172
|
+
return "true|false";
|
|
173
|
+
case "enum":
|
|
174
|
+
return kind.values.map(String).join("|");
|
|
175
|
+
case "array":
|
|
176
|
+
return metavar(kind.item);
|
|
177
|
+
case "object":
|
|
178
|
+
case "json":
|
|
179
|
+
return "json";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function annotate(f, extra) {
|
|
183
|
+
const notes = [];
|
|
184
|
+
if (f.required)
|
|
185
|
+
notes.push("required");
|
|
186
|
+
if (f.hasDefault)
|
|
187
|
+
notes.push(`default: ${JSON.stringify(f.default)}`);
|
|
188
|
+
notes.push(...extra);
|
|
189
|
+
const tail = notes.length > 0 ? `(${notes.join(", ")})` : "";
|
|
190
|
+
return [f.description ?? "", tail].filter((s) => s !== "").join(" ");
|
|
191
|
+
}
|
|
192
|
+
function globalFlagLines(command) {
|
|
193
|
+
const rows = [];
|
|
194
|
+
if (command)
|
|
195
|
+
rows.push(["--input-json <json>", 'Whole input as a JSON object ("-" = read stdin)'], ["-", "Read the whole input as JSON from stdin"]);
|
|
196
|
+
rows.push(["--json", "Print the result as JSON (errors as JSON on stderr)"], ["--yes", "Approve confirmation requests"], ["-h, --help", "Show help"]);
|
|
197
|
+
return table(rows).map((l) => ` ${l}`);
|
|
198
|
+
}
|
|
199
|
+
function isScalarKind(kind) {
|
|
200
|
+
return kind.type === "string" || kind.type === "number" || kind.type === "boolean" || kind.type === "enum";
|
|
201
|
+
}
|
|
202
|
+
function isScalarValue(v) {
|
|
203
|
+
return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
204
|
+
}
|
|
205
|
+
/** 명령어 · 네임스페이스 ID → CLI 경로 ("users.get" → "users get") */
|
|
206
|
+
export function cliPath(id) {
|
|
207
|
+
return id.split(".").join(" ");
|
|
208
|
+
}
|
|
209
|
+
function lastSegment(id) {
|
|
210
|
+
return id.slice(id.lastIndexOf(".") + 1);
|
|
211
|
+
}
|
|
212
|
+
/** _index.md 첫 줄 (markdown 제목 기호 제거) */
|
|
213
|
+
function summaryOf(ns) {
|
|
214
|
+
const line = ns?.description?.split("\n").find((l) => l.trim() !== "");
|
|
215
|
+
return line?.replace(/^#+\s*/, "").trim();
|
|
216
|
+
}
|
|
217
|
+
/** 두 칸 간격 정렬 표. 끝 공백 제거 */
|
|
218
|
+
export function table(rows) {
|
|
219
|
+
const widths = [];
|
|
220
|
+
for (const row of rows)
|
|
221
|
+
row.forEach((cell, i) => (widths[i] = Math.max(widths[i] ?? 0, cell.length)));
|
|
222
|
+
return rows.map((row) => row
|
|
223
|
+
.map((cell, i) => (i === row.length - 1 ? cell : cell.padEnd(widths[i])))
|
|
224
|
+
.join(" ")
|
|
225
|
+
.trimEnd());
|
|
226
|
+
}
|
|
227
|
+
/** 셸에 그대로 붙여 넣을 수 있는 인용 */
|
|
228
|
+
export function shellQuote(value) {
|
|
229
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
|
230
|
+
return value;
|
|
231
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
232
|
+
}
|
|
233
|
+
function finish(lines) {
|
|
234
|
+
while (lines.length > 0 && lines.at(-1) === "")
|
|
235
|
+
lines.pop();
|
|
236
|
+
return `${lines.join("\n")}\n`;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"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","sourcesContent":["import { findCommand } from \"../lookup.ts\"\nimport type { NamespaceInfo, RegisteredCommand, Registry } from \"../registry.ts\"\nimport { cliSpec, type CliField, type FieldKind, type ScalarKind } from \"./args.ts\"\n\n/**\n * help 생성 (D2). 일반 텍스트, ANSI 색 없음, 같은 Registry 에 같은 출력.\n * 대상 = \"\" (루트) · 네임스페이스 ID · 명령어 ID (별칭 포함). prog = 명령어 앞 접두 (\"mext run\" · \"myapp\").\n */\n\nexport interface HelpOptions {\n /** 명령어 경로 앞에 붙는 실행 접두. 예: \"mext run\" */\n prog: string\n}\n\nexport type HelpTarget =\n | { kind: \"namespace\"; namespace: NamespaceInfo; command: RegisteredCommand | undefined }\n | { kind: \"command\"; command: RegisteredCommand }\n\n/** 대상 해석. 네임스페이스가 우선 (users = 네임스페이스 + index.ts 명령어면 둘 다). 없으면 UNKNOWN_COMMAND */\nexport function resolveHelpTarget(registry: Registry, target: string): HelpTarget {\n const ns = registry.namespaces.get(target)\n if (ns) return { kind: \"namespace\", namespace: ns, command: target === \"\" ? undefined : registry.commands.get(target) }\n return { kind: \"command\", command: findCommand(registry, target) }\n}\n\n/** 대상의 help 텍스트 (끝 줄바꿈 포함) */\nexport function renderHelp(registry: Registry, target: string, options: HelpOptions): string {\n const t = resolveHelpTarget(registry, target)\n if (t.kind === \"command\") return renderCommandHelp(t.command, options)\n const text = renderNamespaceHelp(registry, t.namespace, options)\n return t.command ? `${text}\\n${renderCommandHelp(t.command, options)}` : text\n}\n\nexport function renderNamespaceHelp(registry: Registry, ns: NamespaceInfo, options: HelpOptions): string {\n const out: string[] = []\n const path = cliPath(ns.id)\n const prefix = path === \"\" ? options.prog : `${options.prog} ${path}`\n if (ns.id !== \"\") out.push(`Namespace: ${path}`, \"\")\n if (ns.description) out.push(ns.description, \"\")\n out.push(\"Usage:\")\n out.push(` ${prefix} <command> [args] [flags]`)\n if (ns.id === \"\" && ns.namespaces.length > 0) out.push(` ${prefix} <namespace> --help`)\n out.push(` ${prefix} <command> --help`, \"\")\n if (ns.namespaces.length > 0) {\n out.push(\"Namespaces:\")\n out.push(\n ...table(ns.namespaces.map((id) => [lastSegment(id), summaryOf(registry.namespaces.get(id)) ?? \"\"])).map(\n (l) => ` ${l}`,\n ),\n \"\",\n )\n }\n if (ns.commands.length > 0) {\n out.push(\"Commands:\")\n out.push(\n ...table(\n ns.commands.map((id) => {\n const c = registry.commands.get(id)!.command\n return [lastSegment(id), c.description]\n }),\n ).map((l) => ` ${l}`),\n \"\",\n )\n }\n out.push(\"Global flags:\", ...globalFlagLines(false), \"\")\n return finish(out)\n}\n\nexport function renderCommandHelp(entry: RegisteredCommand, options: HelpOptions): string {\n const c = entry.command\n const spec = cliSpec(entry)\n const path = cliPath(entry.id)\n const out: string[] = [c.description, \"\"]\n\n const positional = spec.positional.map((k) => spec.fields.find((f) => f.key === k)!).filter(Boolean)\n const usageArgs = positional.map((f) => {\n const name = f.kind.type === \"array\" ? `${f.flag}...` : f.flag\n return f.required ? `<${name}>` : `[${name}]`\n })\n out.push(\"Usage:\", ` ${[options.prog, path, ...usageArgs, \"[flags]\"].join(\" \")}`, \"\")\n\n if (positional.length > 0) {\n out.push(\"Arguments:\")\n out.push(\n ...table(positional.map((f) => [`<${f.flag}>`, annotate(f, [`or --${f.flag}`])])).map((l) => ` ${l}`),\n \"\",\n )\n }\n\n const flagRows: string[][] = []\n for (const f of spec.fields) {\n if (spec.positional.includes(f.key)) continue\n flagRows.push(...fieldRows(f, \"\"))\n }\n if (flagRows.length > 0) out.push(\"Flags:\", ...table(flagRows).map((l) => ` ${l}`), \"\")\n\n out.push(\"Global flags:\", ...globalFlagLines(true), \"\")\n\n const safety = [c.readOnly ? \"read-only\" : \"writes\"]\n if (c.destructive) safety.push(\"destructive\")\n if (c.requiresConfirmation) safety.push(\"requires confirmation (--yes to approve)\")\n if (c.scopes.length > 0) safety.push(`scopes: ${c.scopes.join(\", \")}`)\n out.push(`Safety: ${safety.join(\", \")}`)\n if (entry.aliases.length > 0) out.push(`Aliases: ${entry.aliases.map(cliPath).join(\", \")}`)\n out.push(\"\")\n\n if (c.examples.length > 0) {\n out.push(\"Examples:\", ...c.examples.map((e) => ` ${formatInvocation(entry, e, options)}`), \"\")\n }\n return finish(out)\n}\n\n/** 입력 객체 → CLI 호출 문자열 (positional · 점 플래그 · 반복 · JSON 문자열) */\nexport function formatInvocation(entry: RegisteredCommand, input: unknown, options: HelpOptions): string {\n const spec = cliSpec(entry)\n const parts = [options.prog, cliPath(entry.id)]\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n return [...parts, \"--input-json\", shellQuote(JSON.stringify(input))].join(\" \")\n }\n const obj = input as Record<string, unknown>\n const flags: string[] = []\n const known = new Set(spec.fields.map((f) => f.key))\n if (Object.keys(obj).some((k) => !known.has(k))) {\n return [...parts, \"--input-json\", shellQuote(JSON.stringify(input))].join(\" \")\n }\n let positionalOpen = true\n for (const key of spec.positional) {\n const f = spec.fields.find((x) => x.key === key)!\n const v = obj[key]\n if (!positionalOpen || v === undefined) {\n positionalOpen = false\n continue\n }\n if (f.kind.type === \"array\" && Array.isArray(v) && key === spec.positional.at(-1)) {\n parts.push(...v.map((x) => shellQuote(String(x))))\n } else if (isScalarKind(f.kind) && isScalarValue(v)) {\n parts.push(shellQuote(String(v)))\n } else {\n positionalOpen = false\n continue\n }\n known.delete(key)\n }\n for (const f of spec.fields) {\n if (!known.has(f.key) || obj[f.key] === undefined) continue\n flags.push(...flagTokens(f, `--${f.flag}`, obj[f.key]))\n }\n return [...parts, ...flags].join(\" \")\n}\n\nfunction flagTokens(f: CliField, flag: string, v: unknown): string[] {\n const kind = f.kind\n if (kind.type === \"boolean\" && typeof v === \"boolean\") return [v ? flag : `--no-${flag.slice(2)}`]\n if (kind.type === \"array\" && Array.isArray(v) && v.every(isScalarValue)) {\n return v.flatMap((x) => (kind.item.type === \"boolean\" ? [`${flag}=${x}`] : [flag, shellQuote(String(x))]))\n }\n if (kind.type === \"object\" && typeof v === \"object\" && v !== null && !Array.isArray(v)) {\n const entries = Object.entries(v as Record<string, unknown>)\n const subs = kind.fields\n if (entries.every(([k]) => subs.some((s) => s.key === k))) {\n return entries\n .filter(([, x]) => x !== undefined)\n .flatMap(([k, x]) => {\n const s = subs.find((y) => y.key === k)!\n return flagTokens(s, `${flag}.${s.flag}`, x)\n })\n }\n }\n if (isScalarKind(kind) && isScalarValue(v)) return [flag, shellQuote(String(v))]\n return [flag, shellQuote(JSON.stringify(v))]\n}\n\nfunction fieldRows(f: CliField, parent: string): string[][] {\n const name = parent === \"\" ? f.flag : `${parent}.${f.flag}`\n const kind = f.kind\n if (kind.type === \"object\") {\n const rows = [[`--${name} <json>`, annotate(f, [])]]\n for (const s of kind.fields) rows.push(...fieldRows(s, name))\n return rows\n }\n if (kind.type === \"boolean\") return [[`--[no-]${name}`, annotate(f, [])]]\n if (kind.type === \"array\") {\n const left = kind.item.type === \"boolean\" ? `--${name}[=true|false]` : `--${name} <${metavar(kind.item)}>`\n return [[left, annotate(f, [\"repeatable\"])]]\n }\n return [[`--${name} <${metavar(kind)}>`, annotate(f, [])]]\n}\n\nfunction metavar(kind: FieldKind): string {\n switch (kind.type) {\n case \"string\":\n return \"string\"\n case \"number\":\n return \"number\"\n case \"boolean\":\n return \"true|false\"\n case \"enum\":\n return kind.values.map(String).join(\"|\")\n case \"array\":\n return metavar(kind.item)\n case \"object\":\n case \"json\":\n return \"json\"\n }\n}\n\nfunction annotate(f: CliField, extra: string[]): string {\n const notes: string[] = []\n if (f.required) notes.push(\"required\")\n if (f.hasDefault) notes.push(`default: ${JSON.stringify(f.default)}`)\n notes.push(...extra)\n const tail = notes.length > 0 ? `(${notes.join(\", \")})` : \"\"\n return [f.description ?? \"\", tail].filter((s) => s !== \"\").join(\" \")\n}\n\nfunction globalFlagLines(command: boolean): string[] {\n const rows: string[][] = []\n if (command) rows.push([\"--input-json <json>\", 'Whole input as a JSON object (\"-\" = read stdin)'], [\"-\", \"Read the whole input as JSON from stdin\"])\n rows.push(\n [\"--json\", \"Print the result as JSON (errors as JSON on stderr)\"],\n [\"--yes\", \"Approve confirmation requests\"],\n [\"-h, --help\", \"Show help\"],\n )\n return table(rows).map((l) => ` ${l}`)\n}\n\nfunction isScalarKind(kind: FieldKind): kind is ScalarKind {\n return kind.type === \"string\" || kind.type === \"number\" || kind.type === \"boolean\" || kind.type === \"enum\"\n}\n\nfunction isScalarValue(v: unknown): v is string | number | boolean {\n return typeof v === \"string\" || typeof v === \"number\" || typeof v === \"boolean\"\n}\n\n/** 명령어 · 네임스페이스 ID → CLI 경로 (\"users.get\" → \"users get\") */\nexport function cliPath(id: string): string {\n return id.split(\".\").join(\" \")\n}\n\nfunction lastSegment(id: string): string {\n return id.slice(id.lastIndexOf(\".\") + 1)\n}\n\n/** _index.md 첫 줄 (markdown 제목 기호 제거) */\nfunction summaryOf(ns: NamespaceInfo | undefined): string | undefined {\n const line = ns?.description?.split(\"\\n\").find((l) => l.trim() !== \"\")\n return line?.replace(/^#+\\s*/, \"\").trim()\n}\n\n/** 두 칸 간격 정렬 표. 끝 공백 제거 */\nexport function table(rows: readonly (readonly string[])[]): string[] {\n const widths: number[] = []\n for (const row of rows) row.forEach((cell, i) => (widths[i] = Math.max(widths[i] ?? 0, cell.length)))\n return rows.map((row) =>\n row\n .map((cell, i) => (i === row.length - 1 ? cell : cell.padEnd(widths[i]!)))\n .join(\" \")\n .trimEnd(),\n )\n}\n\n/** 셸에 그대로 붙여 넣을 수 있는 인용 */\nexport function shellQuote(value: string): string {\n if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nfunction finish(lines: string[]): string {\n while (lines.length > 0 && lines.at(-1) === \"\") lines.pop()\n return `${lines.join(\"\\n\")}\\n`\n}\n"]}
|
package/dist/cli/io.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Readable } from "node:stream";
|
|
2
|
+
export interface CliWriter {
|
|
3
|
+
write(chunk: string): unknown;
|
|
4
|
+
}
|
|
5
|
+
/** CLI 실행 환경. 테스트는 일부만 주입 (나머지 = process) */
|
|
6
|
+
export interface CliIo {
|
|
7
|
+
stdin: Readable;
|
|
8
|
+
stdout: CliWriter;
|
|
9
|
+
stderr: CliWriter;
|
|
10
|
+
/** 대화형 터미널 여부 (stdin · stderr). false 면 ctx.ask = 즉시 오류 (--yes 의 confirm 제외) */
|
|
11
|
+
isTTY: boolean;
|
|
12
|
+
env: Record<string, string | undefined>;
|
|
13
|
+
cwd: string;
|
|
14
|
+
/** abort = SIGINT. ctx.signal 로 전달, exit 130 */
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveIo(io?: Partial<CliIo>): CliIo;
|
|
18
|
+
/** stdin 전체 (stdin - 입력) */
|
|
19
|
+
export declare function readAll(stdin: Readable): Promise<string>;
|
|
20
|
+
/** 대화형 프롬프트용 줄 읽기. 한 실행에서 readline 하나를 공유 (버퍼 유실 방지) */
|
|
21
|
+
export interface LineReader {
|
|
22
|
+
/** 프롬프트를 stderr 에 쓰고 한 줄 읽기. EOF = undefined. signal abort = undefined */
|
|
23
|
+
question(prompt: string, signal?: AbortSignal): Promise<string | undefined>;
|
|
24
|
+
close(): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function createLineReader(io: CliIo): LineReader;
|
package/dist/cli/io.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
export function resolveIo(io = {}) {
|
|
3
|
+
return {
|
|
4
|
+
stdin: io.stdin ?? process.stdin,
|
|
5
|
+
stdout: io.stdout ?? process.stdout,
|
|
6
|
+
stderr: io.stderr ?? process.stderr,
|
|
7
|
+
isTTY: io.isTTY ?? (process.stdin.isTTY === true && process.stderr.isTTY === true),
|
|
8
|
+
env: io.env ?? process.env,
|
|
9
|
+
cwd: io.cwd ?? process.cwd(),
|
|
10
|
+
signal: io.signal,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** stdin 전체 (stdin - 입력) */
|
|
14
|
+
export async function readAll(stdin) {
|
|
15
|
+
let text = "";
|
|
16
|
+
for await (const chunk of stdin)
|
|
17
|
+
text += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
export function createLineReader(io) {
|
|
21
|
+
let rl;
|
|
22
|
+
const lines = [];
|
|
23
|
+
const waiting = [];
|
|
24
|
+
let closed = false;
|
|
25
|
+
const open = () => {
|
|
26
|
+
if (rl || closed)
|
|
27
|
+
return;
|
|
28
|
+
rl = createInterface({ input: io.stdin, terminal: false });
|
|
29
|
+
rl.on("line", (line) => {
|
|
30
|
+
const next = waiting.shift();
|
|
31
|
+
if (next)
|
|
32
|
+
next(line);
|
|
33
|
+
else
|
|
34
|
+
lines.push(line);
|
|
35
|
+
});
|
|
36
|
+
rl.on("close", () => {
|
|
37
|
+
closed = true;
|
|
38
|
+
for (const w of waiting.splice(0))
|
|
39
|
+
w(undefined);
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
question(prompt, signal) {
|
|
44
|
+
io.stderr.write(prompt);
|
|
45
|
+
if (lines.length > 0)
|
|
46
|
+
return Promise.resolve(lines.shift());
|
|
47
|
+
open();
|
|
48
|
+
if (closed)
|
|
49
|
+
return Promise.resolve(undefined);
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const done = (line) => {
|
|
52
|
+
signal?.removeEventListener("abort", onAbort);
|
|
53
|
+
resolve(line);
|
|
54
|
+
};
|
|
55
|
+
const onAbort = () => {
|
|
56
|
+
const i = waiting.indexOf(done);
|
|
57
|
+
if (i >= 0)
|
|
58
|
+
waiting.splice(i, 1);
|
|
59
|
+
done(undefined);
|
|
60
|
+
};
|
|
61
|
+
if (signal?.aborted)
|
|
62
|
+
return done(undefined);
|
|
63
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
64
|
+
waiting.push(done);
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
close() {
|
|
68
|
+
closed = true;
|
|
69
|
+
rl?.close();
|
|
70
|
+
rl = undefined;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAA;AAoB/D,MAAM,UAAU,SAAS,CAAC,EAAE,GAAmB,EAAE;IAC/C,OAAO;QACL,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QAChC,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACnC,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACnC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;QAClF,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;QAC1B,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QAC5B,MAAM,EAAE,EAAE,CAAC,MAAM;KAClB,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAe;IAC3C,IAAI,IAAI,GAAG,EAAE,CAAA;IACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK;QAAE,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAmB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9H,OAAO,IAAI,CAAA;AACb,CAAC;AASD,MAAM,UAAU,gBAAgB,CAAC,EAAS;IACxC,IAAI,EAAyB,CAAA;IAC7B,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,OAAO,GAA2C,EAAE,CAAA;IAC1D,IAAI,MAAM,GAAG,KAAK,CAAA;IAElB,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,IAAI,EAAE,IAAI,MAAM;YAAE,OAAM;QACxB,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1D,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACrB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,CAAA;YAC5B,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,CAAA;;gBACf,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvB,CAAC,CAAC,CAAA;QACF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClB,MAAM,GAAG,IAAI,CAAA;YACb,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAAE,CAAC,CAAC,SAAS,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,OAAO;QACL,QAAQ,CAAC,MAAM,EAAE,MAAM;YACrB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACvB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;YAC3D,IAAI,EAAE,CAAA;YACN,IAAI,MAAM;gBAAE,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;YAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,MAAM,IAAI,GAAG,CAAC,IAAwB,EAAQ,EAAE;oBAC9C,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;oBAC7C,OAAO,CAAC,IAAI,CAAC,CAAA;gBACf,CAAC,CAAA;gBACD,MAAM,OAAO,GAAG,GAAS,EAAE;oBACzB,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;oBAC/B,IAAI,CAAC,IAAI,CAAC;wBAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;oBAChC,IAAI,CAAC,SAAS,CAAC,CAAA;gBACjB,CAAC,CAAA;gBACD,IAAI,MAAM,EAAE,OAAO;oBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAA;gBAC3C,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;gBAC1D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACpB,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,KAAK;YACH,MAAM,GAAG,IAAI,CAAA;YACb,EAAE,EAAE,KAAK,EAAE,CAAA;YACX,EAAE,GAAG,SAAS,CAAA;QAChB,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import { createInterface, type Interface } from \"node:readline\"\nimport type { Readable } from \"node:stream\"\n\nexport interface CliWriter {\n write(chunk: string): unknown\n}\n\n/** CLI 실행 환경. 테스트는 일부만 주입 (나머지 = process) */\nexport interface CliIo {\n stdin: Readable\n stdout: CliWriter\n stderr: CliWriter\n /** 대화형 터미널 여부 (stdin · stderr). false 면 ctx.ask = 즉시 오류 (--yes 의 confirm 제외) */\n isTTY: boolean\n env: Record<string, string | undefined>\n cwd: string\n /** abort = SIGINT. ctx.signal 로 전달, exit 130 */\n signal?: AbortSignal\n}\n\nexport function resolveIo(io: Partial<CliIo> = {}): CliIo {\n return {\n stdin: io.stdin ?? process.stdin,\n stdout: io.stdout ?? process.stdout,\n stderr: io.stderr ?? process.stderr,\n isTTY: io.isTTY ?? (process.stdin.isTTY === true && process.stderr.isTTY === true),\n env: io.env ?? process.env,\n cwd: io.cwd ?? process.cwd(),\n signal: io.signal,\n }\n}\n\n/** stdin 전체 (stdin - 입력) */\nexport async function readAll(stdin: Readable): Promise<string> {\n let text = \"\"\n for await (const chunk of stdin) text += typeof chunk === \"string\" ? chunk : Buffer.from(chunk as Uint8Array).toString(\"utf8\")\n return text\n}\n\n/** 대화형 프롬프트용 줄 읽기. 한 실행에서 readline 하나를 공유 (버퍼 유실 방지) */\nexport interface LineReader {\n /** 프롬프트를 stderr 에 쓰고 한 줄 읽기. EOF = undefined. signal abort = undefined */\n question(prompt: string, signal?: AbortSignal): Promise<string | undefined>\n close(): void\n}\n\nexport function createLineReader(io: CliIo): LineReader {\n let rl: Interface | undefined\n const lines: string[] = []\n const waiting: ((line: string | undefined) => void)[] = []\n let closed = false\n\n const open = (): void => {\n if (rl || closed) return\n rl = createInterface({ input: io.stdin, terminal: false })\n rl.on(\"line\", (line) => {\n const next = waiting.shift()\n if (next) next(line)\n else lines.push(line)\n })\n rl.on(\"close\", () => {\n closed = true\n for (const w of waiting.splice(0)) w(undefined)\n })\n }\n\n return {\n question(prompt, signal) {\n io.stderr.write(prompt)\n if (lines.length > 0) return Promise.resolve(lines.shift())\n open()\n if (closed) return Promise.resolve(undefined)\n return new Promise((resolve) => {\n const done = (line: string | undefined): void => {\n signal?.removeEventListener(\"abort\", onAbort)\n resolve(line)\n }\n const onAbort = (): void => {\n const i = waiting.indexOf(done)\n if (i >= 0) waiting.splice(i, 1)\n done(undefined)\n }\n if (signal?.aborted) return done(undefined)\n signal?.addEventListener(\"abort\", onAbort, { once: true })\n waiting.push(done)\n })\n },\n close() {\n closed = true\n rl?.close()\n rl = undefined\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type BrowserOpener } from "../remote/browser.ts";
|
|
2
|
+
import { type CliIo } from "./io.ts";
|
|
3
|
+
/**
|
|
4
|
+
* login · logout 명령 공통 설정. mext CLI = { bin: "mext" }, 고객 CLI = { bin, url } (설정의 cli.bin · cli.url).
|
|
5
|
+
* bin = 안내 문구의 실행 파일 이름 · 환경변수 접두 (<BIN>_URL · <BIN>_CREDENTIALS_FILE · <BIN>_NO_KEYRING) ·
|
|
6
|
+
* 자격 증명 위치 (~/.config/<bin> · 키체인 service "<bin> credentials") · 동적 등록 client_name.
|
|
7
|
+
*/
|
|
8
|
+
export interface AuthCommandOptions {
|
|
9
|
+
bin: string;
|
|
10
|
+
/** 고정 서버 URL (고객 CLI). --url 로 덮어쓸 수 있다. 없으면 --url 또는 <BIN>_URL 필수 */
|
|
11
|
+
url?: string;
|
|
12
|
+
/** 브라우저 열기 (테스트 주입). 기본 = BROWSER · open · xdg-open */
|
|
13
|
+
openBrowser?: BrowserOpener;
|
|
14
|
+
/** 브라우저 판정용 (테스트 주입). 기본 process.platform */
|
|
15
|
+
platform?: NodeJS.Platform;
|
|
16
|
+
/** 인가 서버 요청 (테스트 주입) */
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
/** device flow polling 대기 (테스트 주입) */
|
|
19
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* <bin> login — MCP authorization (OAuth 2.1 · PKCE) 로 토큰 발급 · 저장. 서버 URL 별 (키체인 · credentials.json).
|
|
23
|
+
* redirect = loopback (127.0.0.1) 또는 붙여넣은 redirect URL (TTY). device flow = 인가 서버가 지원할 때 (--device,
|
|
24
|
+
* 브라우저를 열지 않는 환경이면 자동). --with-token = stdin 한 줄을 API key 로 저장.
|
|
25
|
+
*/
|
|
26
|
+
export declare function loginCommand(options: AuthCommandOptions): (argv: readonly string[], io: CliIo) => Promise<number>;
|
|
27
|
+
/** mext login */
|
|
28
|
+
export declare const runLogin: (argv: readonly string[], io: CliIo) => Promise<number>;
|
|
29
|
+
/** 서버 URL = --url → 고정 URL (고객 CLI) → <BIN>_URL */
|
|
30
|
+
export declare function resolveServerUrl(options: AuthCommandOptions, flag: string | undefined, io: CliIo): string | {
|
|
31
|
+
error: string;
|
|
32
|
+
};
|