@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.
Files changed (155) hide show
  1. package/dist/build-cli.d.ts +39 -0
  2. package/dist/build-cli.js +160 -0
  3. package/dist/build-cli.js.map +1 -0
  4. package/dist/build.d.ts +65 -0
  5. package/dist/build.js +252 -0
  6. package/dist/build.js.map +1 -0
  7. package/dist/cli/args.d.ts +107 -0
  8. package/dist/cli/args.js +621 -0
  9. package/dist/cli/args.js.map +1 -0
  10. package/dist/cli/bin.d.ts +2 -0
  11. package/dist/cli/bin.js +13 -0
  12. package/dist/cli/bin.js.map +1 -0
  13. package/dist/cli/build.d.ts +11 -0
  14. package/dist/cli/build.js +87 -0
  15. package/dist/cli/build.js.map +1 -0
  16. package/dist/cli/dev-lock.d.ts +23 -0
  17. package/dist/cli/dev-lock.js +68 -0
  18. package/dist/cli/dev-lock.js.map +1 -0
  19. package/dist/cli/dev-server.d.ts +20 -0
  20. package/dist/cli/dev-server.js +74 -0
  21. package/dist/cli/dev-server.js.map +1 -0
  22. package/dist/cli/dev.d.ts +21 -0
  23. package/dist/cli/dev.js +564 -0
  24. package/dist/cli/dev.js.map +1 -0
  25. package/dist/cli/help.d.ts +31 -0
  26. package/dist/cli/help.js +238 -0
  27. package/dist/cli/help.js.map +1 -0
  28. package/dist/cli/io.d.ts +26 -0
  29. package/dist/cli/io.js +74 -0
  30. package/dist/cli/io.js.map +1 -0
  31. package/dist/cli/login.d.ts +32 -0
  32. package/dist/cli/login.js +284 -0
  33. package/dist/cli/login.js.map +1 -0
  34. package/dist/cli/logout.d.ts +9 -0
  35. package/dist/cli/logout.js +105 -0
  36. package/dist/cli/logout.js.map +1 -0
  37. package/dist/cli/main.d.ts +19 -0
  38. package/dist/cli/main.js +77 -0
  39. package/dist/cli/main.js.map +1 -0
  40. package/dist/cli/mcp.d.ts +16 -0
  41. package/dist/cli/mcp.js +92 -0
  42. package/dist/cli/mcp.js.map +1 -0
  43. package/dist/cli/output.d.ts +34 -0
  44. package/dist/cli/output.js +172 -0
  45. package/dist/cli/output.js.map +1 -0
  46. package/dist/cli/remote.d.ts +17 -0
  47. package/dist/cli/remote.js +35 -0
  48. package/dist/cli/remote.js.map +1 -0
  49. package/dist/cli/run.d.ts +14 -0
  50. package/dist/cli/run.js +218 -0
  51. package/dist/cli/run.js.map +1 -0
  52. package/dist/cli/start.d.ts +8 -0
  53. package/dist/cli/start.js +37 -0
  54. package/dist/cli/start.js.map +1 -0
  55. package/dist/client/cache.d.ts +104 -0
  56. package/dist/client/cache.js +208 -0
  57. package/dist/client/cache.js.map +1 -0
  58. package/dist/client/main.d.ts +25 -0
  59. package/dist/client/main.js +100 -0
  60. package/dist/client/main.js.map +1 -0
  61. package/dist/client/manifest.d.ts +19 -0
  62. package/dist/client/manifest.js +68 -0
  63. package/dist/client/manifest.js.map +1 -0
  64. package/dist/client/runner.d.ts +49 -0
  65. package/dist/client/runner.js +562 -0
  66. package/dist/client/runner.js.map +1 -0
  67. package/dist/command.d.ts +75 -0
  68. package/dist/command.js +44 -0
  69. package/dist/command.js.map +1 -0
  70. package/dist/config.d.ts +53 -0
  71. package/dist/config.js +122 -0
  72. package/dist/config.js.map +1 -0
  73. package/dist/errors.d.ts +74 -0
  74. package/dist/errors.js +38 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/ids.d.ts +6 -0
  77. package/dist/ids.js +12 -0
  78. package/dist/ids.js.map +1 -0
  79. package/dist/index.d.ts +45 -0
  80. package/dist/index.js +27 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/instrumentation.d.ts +97 -0
  83. package/dist/instrumentation.js +165 -0
  84. package/dist/instrumentation.js.map +1 -0
  85. package/dist/invoke.d.ts +64 -0
  86. package/dist/invoke.js +152 -0
  87. package/dist/invoke.js.map +1 -0
  88. package/dist/lookup.d.ts +9 -0
  89. package/dist/lookup.js +27 -0
  90. package/dist/lookup.js.map +1 -0
  91. package/dist/manifest.d.ts +68 -0
  92. package/dist/manifest.js +77 -0
  93. package/dist/manifest.js.map +1 -0
  94. package/dist/mcp/constants.d.ts +9 -0
  95. package/dist/mcp/constants.js +10 -0
  96. package/dist/mcp/constants.js.map +1 -0
  97. package/dist/mcp/dispatch.d.ts +66 -0
  98. package/dist/mcp/dispatch.js +249 -0
  99. package/dist/mcp/dispatch.js.map +1 -0
  100. package/dist/mcp/help.d.ts +57 -0
  101. package/dist/mcp/help.js +136 -0
  102. package/dist/mcp/help.js.map +1 -0
  103. package/dist/mcp/http.d.ts +92 -0
  104. package/dist/mcp/http.js +366 -0
  105. package/dist/mcp/http.js.map +1 -0
  106. package/dist/mcp/index.d.ts +15 -0
  107. package/dist/mcp/index.js +9 -0
  108. package/dist/mcp/index.js.map +1 -0
  109. package/dist/mcp/install.d.ts +17 -0
  110. package/dist/mcp/install.js +36 -0
  111. package/dist/mcp/install.js.map +1 -0
  112. package/dist/mcp/mrtr.d.ts +82 -0
  113. package/dist/mcp/mrtr.js +130 -0
  114. package/dist/mcp/mrtr.js.map +1 -0
  115. package/dist/mcp/server.d.ts +58 -0
  116. package/dist/mcp/server.js +204 -0
  117. package/dist/mcp/server.js.map +1 -0
  118. package/dist/mcp/stdio.d.ts +57 -0
  119. package/dist/mcp/stdio.js +79 -0
  120. package/dist/mcp/stdio.js.map +1 -0
  121. package/dist/principal.d.ts +16 -0
  122. package/dist/principal.js +24 -0
  123. package/dist/principal.js.map +1 -0
  124. package/dist/registry.d.ts +90 -0
  125. package/dist/registry.js +169 -0
  126. package/dist/registry.js.map +1 -0
  127. package/dist/remote/browser.d.ts +15 -0
  128. package/dist/remote/browser.js +47 -0
  129. package/dist/remote/browser.js.map +1 -0
  130. package/dist/remote/client.d.ts +113 -0
  131. package/dist/remote/client.js +312 -0
  132. package/dist/remote/client.js.map +1 -0
  133. package/dist/remote/credentials.d.ts +48 -0
  134. package/dist/remote/credentials.js +196 -0
  135. package/dist/remote/credentials.js.map +1 -0
  136. package/dist/remote/index.d.ts +8 -0
  137. package/dist/remote/index.js +5 -0
  138. package/dist/remote/index.js.map +1 -0
  139. package/dist/remote/oauth.d.ts +186 -0
  140. package/dist/remote/oauth.js +507 -0
  141. package/dist/remote/oauth.js.map +1 -0
  142. package/dist/scan.d.ts +34 -0
  143. package/dist/scan.js +155 -0
  144. package/dist/scan.js.map +1 -0
  145. package/dist/start.d.ts +63 -0
  146. package/dist/start.js +267 -0
  147. package/dist/start.js.map +1 -0
  148. package/dist/suggest.d.ts +7 -0
  149. package/dist/suggest.js +45 -0
  150. package/dist/suggest.js.map +1 -0
  151. package/dist/version.d.ts +4 -0
  152. package/dist/version.js +13 -0
  153. package/dist/version.js.map +1 -0
  154. package/package.json +49 -4
  155. package/README.md +0 -3
package/dist/scan.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { type BuildIssue } from "./errors.ts";
2
+ import { type Registry, type RegistryCommandInput, type RegistryNamespaceInput } from "./registry.ts";
3
+ export interface ScannedFile {
4
+ id: string;
5
+ /** commands/ 기준 상대 경로 (posix) */
6
+ file: string;
7
+ }
8
+ export interface ScanResult {
9
+ /** commands/ 절대 경로 */
10
+ dir: string;
11
+ commands: RegistryCommandInput[];
12
+ /** _index.md 가 있는 네임스페이스. "" = 루트 (commands/_index.md) */
13
+ namespaces: RegistryNamespaceInput[];
14
+ }
15
+ /**
16
+ * commands/ 파일 트리를 파일 규칙으로 해석 (import 없음).
17
+ * index.ts = 네임스페이스 자체 명령어 · `_` `.` 접두 = private · *.test.ts *.spec.ts *.d.ts 제외 ·
18
+ * 같은 레벨 x.ts + x/ = 오류 · 세그먼트 kebab-case.
19
+ */
20
+ export declare function walkCommands(dir: string): {
21
+ files: ScannedFile[];
22
+ namespaces: RegistryNamespaceInput[];
23
+ };
24
+ /** 파일 트리 해석 + 각 파일의 default export (defineCommand) import */
25
+ export declare function scanCommands(dir: string): Promise<ScanResult>;
26
+ /**
27
+ * scanCommands 의 수집형 (mext build 의 일괄 검증용). 던지지 않는다.
28
+ * 파일 규칙 위반이 있어도 해석된 파일은 import 해 LOAD_FAILED · INVALID_COMMAND 까지 issues 에 모은다
29
+ */
30
+ export declare function collectCommands(dir: string): Promise<ScanResult & {
31
+ issues: BuildIssue[];
32
+ }>;
33
+ /** 개발 모드 런타임 스캔: commands/ → Registry */
34
+ export declare function loadRegistry(dir: string): Promise<Registry>;
package/dist/scan.js ADDED
@@ -0,0 +1,155 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { readdirSync, readFileSync, statSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import { isCommand } from "./command.js";
13
+ import { MextBuildError } from "./errors.js";
14
+ import { isSegment } from "./ids.js";
15
+ import { createRegistry } from "./registry.js";
16
+ const EXCLUDED_SUFFIXES = [".test.ts", ".spec.ts", ".d.ts"];
17
+ /**
18
+ * commands/ 파일 트리를 파일 규칙으로 해석 (import 없음).
19
+ * index.ts = 네임스페이스 자체 명령어 · `_` `.` 접두 = private · *.test.ts *.spec.ts *.d.ts 제외 ·
20
+ * 같은 레벨 x.ts + x/ = 오류 · 세그먼트 kebab-case.
21
+ */
22
+ export function walkCommands(dir) {
23
+ const { files, namespaces, issues } = collectWalk(dir);
24
+ if (issues.length > 0)
25
+ throw new MextBuildError(issues);
26
+ return { files, namespaces };
27
+ }
28
+ function collectWalk(dir) {
29
+ const root = resolve(dir);
30
+ const files = [];
31
+ const namespaces = [];
32
+ const issues = [];
33
+ if (!isDirectory(root)) {
34
+ return { files, namespaces, issues: [{ code: "COMMANDS_DIR_MISSING", message: `commands directory not found: ${root}` }] };
35
+ }
36
+ const visit = (abs, segments) => {
37
+ const entries = readdirSync(abs, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1));
38
+ const fileNames = new Set(entries.filter((e) => isFileEntry(abs, e)).map((e) => e.name));
39
+ for (const entry of entries) {
40
+ const name = entry.name;
41
+ const rel = [...segments, name].join("/");
42
+ const childAbs = join(abs, name);
43
+ const isDir = isDirEntry(abs, entry);
44
+ if (name.startsWith("_") || name.startsWith(".")) {
45
+ if (name === "_index.md" && !isDir) {
46
+ namespaces.push({ id: segments.join("."), description: readFileSync(childAbs, "utf8").trim() });
47
+ }
48
+ continue;
49
+ }
50
+ if (isDir) {
51
+ if (!isSegment(name)) {
52
+ issues.push({ code: "INVALID_SEGMENT", message: `directory "${name}" is not kebab-case`, file: rel });
53
+ continue;
54
+ }
55
+ if (fileNames.has(`${name}.ts`)) {
56
+ issues.push({
57
+ code: "PATH_COLLISION",
58
+ message: `${[...segments, `${name}.ts`].join("/")} and ${rel}/ coexist. Use ${rel}/index.ts for the namespace command`,
59
+ file: rel,
60
+ });
61
+ continue;
62
+ }
63
+ visit(childAbs, [...segments, name]);
64
+ continue;
65
+ }
66
+ if (!name.endsWith(".ts") || EXCLUDED_SUFFIXES.some((s) => name.endsWith(s)))
67
+ continue;
68
+ if (!isFileEntry(abs, entry))
69
+ continue;
70
+ const base = name.slice(0, -".ts".length);
71
+ if (base === "index") {
72
+ if (segments.length === 0) {
73
+ issues.push({ code: "ROOT_INDEX", message: "commands/index.ts has no ID", file: rel });
74
+ continue;
75
+ }
76
+ files.push({ id: segments.join("."), file: rel });
77
+ continue;
78
+ }
79
+ if (!isSegment(base)) {
80
+ issues.push({ code: "INVALID_SEGMENT", message: `file "${name}" is not kebab-case`, file: rel });
81
+ continue;
82
+ }
83
+ files.push({ id: [...segments, base].join("."), file: rel });
84
+ }
85
+ };
86
+ visit(root, []);
87
+ return { files, namespaces, issues };
88
+ }
89
+ /** 파일 트리 해석 + 각 파일의 default export (defineCommand) import */
90
+ export async function scanCommands(dir) {
91
+ const root = resolve(dir);
92
+ const { files, namespaces } = walkCommands(root);
93
+ const { commands, issues } = await importCommands(root, files);
94
+ if (issues.length > 0)
95
+ throw new MextBuildError(issues);
96
+ return { dir: root, commands, namespaces };
97
+ }
98
+ /**
99
+ * scanCommands 의 수집형 (mext build 의 일괄 검증용). 던지지 않는다.
100
+ * 파일 규칙 위반이 있어도 해석된 파일은 import 해 LOAD_FAILED · INVALID_COMMAND 까지 issues 에 모은다
101
+ */
102
+ export async function collectCommands(dir) {
103
+ const root = resolve(dir);
104
+ const walked = collectWalk(root);
105
+ const { commands, issues } = await importCommands(root, walked.files);
106
+ return { dir: root, commands, namespaces: walked.namespaces, issues: [...walked.issues, ...issues] };
107
+ }
108
+ async function importCommands(root, files) {
109
+ const issues = [];
110
+ const commands = [];
111
+ for (const { id, file } of files) {
112
+ let mod;
113
+ try {
114
+ mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(root, file)).href)));
115
+ }
116
+ catch (error) {
117
+ issues.push({ code: "LOAD_FAILED", message: error instanceof Error ? error.message : String(error), file, id });
118
+ continue;
119
+ }
120
+ if (!isCommand(mod.default)) {
121
+ issues.push({ code: "INVALID_COMMAND", message: "default export is not a defineCommand result", file, id });
122
+ continue;
123
+ }
124
+ commands.push({ id, file, command: mod.default });
125
+ }
126
+ return { commands, issues };
127
+ }
128
+ /** 개발 모드 런타임 스캔: commands/ → Registry */
129
+ export async function loadRegistry(dir) {
130
+ return createRegistry(await scanCommands(dir));
131
+ }
132
+ function isDirectory(path) {
133
+ try {
134
+ return statSync(path).isDirectory();
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ function isDirEntry(parent, entry) {
141
+ return entry.isDirectory() || (entry.isSymbolicLink() && isDirectory(join(parent, entry.name)));
142
+ }
143
+ function isFileEntry(parent, entry) {
144
+ if (entry.isFile())
145
+ return true;
146
+ if (!entry.isSymbolicLink())
147
+ return false;
148
+ try {
149
+ return statSync(join(parent, entry.name)).isFile();
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
155
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync, statSync, type Dirent } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { isCommand } from \"./command.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport { isSegment } from \"./ids.ts\"\nimport { createRegistry, type Registry, type RegistryCommandInput, type RegistryNamespaceInput } from \"./registry.ts\"\n\nexport interface ScannedFile {\n id: string\n /** commands/ 기준 상대 경로 (posix) */\n file: string\n}\n\nexport interface ScanResult {\n /** commands/ 절대 경로 */\n dir: string\n commands: RegistryCommandInput[]\n /** _index.md 가 있는 네임스페이스. \"\" = 루트 (commands/_index.md) */\n namespaces: RegistryNamespaceInput[]\n}\n\nconst EXCLUDED_SUFFIXES = [\".test.ts\", \".spec.ts\", \".d.ts\"]\n\n/**\n * commands/ 파일 트리를 파일 규칙으로 해석 (import 없음).\n * index.ts = 네임스페이스 자체 명령어 · `_` `.` 접두 = private · *.test.ts *.spec.ts *.d.ts 제외 ·\n * 같은 레벨 x.ts + x/ = 오류 · 세그먼트 kebab-case.\n */\nexport function walkCommands(dir: string): { files: ScannedFile[]; namespaces: RegistryNamespaceInput[] } {\n const { files, namespaces, issues } = collectWalk(dir)\n if (issues.length > 0) throw new MextBuildError(issues)\n return { files, namespaces }\n}\n\nfunction collectWalk(dir: string): { files: ScannedFile[]; namespaces: RegistryNamespaceInput[]; issues: BuildIssue[] } {\n const root = resolve(dir)\n const files: ScannedFile[] = []\n const namespaces: RegistryNamespaceInput[] = []\n const issues: BuildIssue[] = []\n\n if (!isDirectory(root)) {\n return { files, namespaces, issues: [{ code: \"COMMANDS_DIR_MISSING\", message: `commands directory not found: ${root}` }] }\n }\n\n const visit = (abs: string, segments: string[]): void => {\n const entries = readdirSync(abs, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))\n const fileNames = new Set(entries.filter((e) => isFileEntry(abs, e)).map((e) => e.name))\n for (const entry of entries) {\n const name = entry.name\n const rel = [...segments, name].join(\"/\")\n const childAbs = join(abs, name)\n const isDir = isDirEntry(abs, entry)\n\n if (name.startsWith(\"_\") || name.startsWith(\".\")) {\n if (name === \"_index.md\" && !isDir) {\n namespaces.push({ id: segments.join(\".\"), description: readFileSync(childAbs, \"utf8\").trim() })\n }\n continue\n }\n\n if (isDir) {\n if (!isSegment(name)) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `directory \"${name}\" is not kebab-case`, file: rel })\n continue\n }\n if (fileNames.has(`${name}.ts`)) {\n issues.push({\n code: \"PATH_COLLISION\",\n message: `${[...segments, `${name}.ts`].join(\"/\")} and ${rel}/ coexist. Use ${rel}/index.ts for the namespace command`,\n file: rel,\n })\n continue\n }\n visit(childAbs, [...segments, name])\n continue\n }\n\n if (!name.endsWith(\".ts\") || EXCLUDED_SUFFIXES.some((s) => name.endsWith(s))) continue\n if (!isFileEntry(abs, entry)) continue\n const base = name.slice(0, -\".ts\".length)\n if (base === \"index\") {\n if (segments.length === 0) {\n issues.push({ code: \"ROOT_INDEX\", message: \"commands/index.ts has no ID\", file: rel })\n continue\n }\n files.push({ id: segments.join(\".\"), file: rel })\n continue\n }\n if (!isSegment(base)) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `file \"${name}\" is not kebab-case`, file: rel })\n continue\n }\n files.push({ id: [...segments, base].join(\".\"), file: rel })\n }\n }\n\n visit(root, [])\n return { files, namespaces, issues }\n}\n\n/** 파일 트리 해석 + 각 파일의 default export (defineCommand) import */\nexport async function scanCommands(dir: string): Promise<ScanResult> {\n const root = resolve(dir)\n const { files, namespaces } = walkCommands(root)\n const { commands, issues } = await importCommands(root, files)\n if (issues.length > 0) throw new MextBuildError(issues)\n return { dir: root, commands, namespaces }\n}\n\n/**\n * scanCommands 의 수집형 (mext build 의 일괄 검증용). 던지지 않는다.\n * 파일 규칙 위반이 있어도 해석된 파일은 import 해 LOAD_FAILED · INVALID_COMMAND 까지 issues 에 모은다\n */\nexport async function collectCommands(dir: string): Promise<ScanResult & { issues: BuildIssue[] }> {\n const root = resolve(dir)\n const walked = collectWalk(root)\n const { commands, issues } = await importCommands(root, walked.files)\n return { dir: root, commands, namespaces: walked.namespaces, issues: [...walked.issues, ...issues] }\n}\n\nasync function importCommands(\n root: string,\n files: readonly ScannedFile[],\n): Promise<{ commands: RegistryCommandInput[]; issues: BuildIssue[] }> {\n const issues: BuildIssue[] = []\n const commands: RegistryCommandInput[] = []\n for (const { id, file } of files) {\n let mod: { default?: unknown }\n try {\n mod = (await import(pathToFileURL(join(root, file)).href)) as { default?: unknown }\n } catch (error) {\n issues.push({ code: \"LOAD_FAILED\", message: error instanceof Error ? error.message : String(error), file, id })\n continue\n }\n if (!isCommand(mod.default)) {\n issues.push({ code: \"INVALID_COMMAND\", message: \"default export is not a defineCommand result\", file, id })\n continue\n }\n commands.push({ id, file, command: mod.default })\n }\n return { commands, issues }\n}\n\n/** 개발 모드 런타임 스캔: commands/ → Registry */\nexport async function loadRegistry(dir: string): Promise<Registry> {\n return createRegistry(await scanCommands(dir))\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction isDirEntry(parent: string, entry: Dirent): boolean {\n return entry.isDirectory() || (entry.isSymbolicLink() && isDirectory(join(parent, entry.name)))\n}\n\nfunction isFileEntry(parent: string, entry: Dirent): boolean {\n if (entry.isFile()) return true\n if (!entry.isSymbolicLink()) return false\n try {\n return statSync(join(parent, entry.name)).isFile()\n } catch {\n return false\n }\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import { type MextConfig } from "./config.ts";
2
+ import { type Instrumentation } from "./instrumentation.ts";
3
+ import { type BuildManifest } from "./manifest.ts";
4
+ import { type Registry } from "./registry.ts";
5
+ /**
6
+ * @obov/mext/start: .mext/ (mext build 산출) 실행 런타임. 소스 스캔 · TypeScript 없음.
7
+ * 생성 파일 계약 (mext build 가 쓰는 server.js · handler.js, 바꾸면 기존 빌드가 깨진다):
8
+ * server.js process.exitCode = await startFromBuild(new URL(".", import.meta.url), { argv: process.argv.slice(2), env: process.env })
9
+ * handler.js export default createFetchHandler(new URL(".", import.meta.url))
10
+ *
11
+ * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.
12
+ * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)
13
+ */
14
+ export interface StartOptions {
15
+ /** server.js · mext start 뒤 인자. --stdio · --port <n> · --host <h> */
16
+ argv?: readonly string[];
17
+ /** PORT · HOST. 기본 process.env */
18
+ env?: Record<string, string | undefined>;
19
+ /** abort = 종료. 기본 SIGINT · SIGTERM */
20
+ signal?: AbortSignal;
21
+ /** 기본 process.stderr */
22
+ stderr?: {
23
+ write(chunk: string): unknown;
24
+ };
25
+ }
26
+ export interface FetchHandler {
27
+ /** 거부하지 않는다. 초기화 실패 · 내부 오류 = 500 JSON-RPC 오류 */
28
+ fetch(request: Request): Promise<Response>;
29
+ }
30
+ /** .mext/ 로드 결과 */
31
+ export interface LoadedBuild {
32
+ manifest: BuildManifest;
33
+ config: MextConfig;
34
+ registry: Registry;
35
+ instrumentation: Instrumentation;
36
+ }
37
+ export interface LoadBuildOptions {
38
+ /** 명령어 모듈 import (테스트 계측). 기본 = 동적 import */
39
+ importModule?: (href: string) => Promise<unknown>;
40
+ }
41
+ /** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */
42
+ export declare function startFromBuild(dir: string | URL, options?: StartOptions): Promise<number>;
43
+ /**
44
+ * handler.js 의 Web fetch handler ({ fetch(request) → Response }). 동기 반환.
45
+ * D7: 초기화 (manifest 검증 · instrumentation register · config · Registry) = 첫 요청에서 1회 (memoized promise).
46
+ * 초기화 실패 = 그 요청 500 + JSON-RPC 오류 (data.loadError), 다음 요청에서 재시도.
47
+ * MCP 처리 (Origin · auth · 헤더 검증 · SSE progress · MRTR) = node 서버와 같은 createMcpFetchHandler
48
+ */
49
+ export declare function createFetchHandler(dir: string | URL, options?: LoadBuildOptions): FetchHandler;
50
+ /** .mext/manifest.json 읽기 + 검증 (없음 · schemaVersion · mextVersion 불일치 = 오류) */
51
+ export declare function readBuildManifest(dir: string | URL): BuildManifest;
52
+ /** manifest 검증 → instrumentation.js (register, 사용자 코드보다 먼저) → config.js → lazy Registry */
53
+ export declare function loadBuild(dir: string | URL, options?: LoadBuildOptions): Promise<LoadedBuild>;
54
+ /** manifest → lazy Registry. 명령어 모듈 (manifest.commands[].module) 은 load() 에서 import */
55
+ export declare function registryFromManifest(manifest: BuildManifest, dir: string | URL, options?: LoadBuildOptions): Registry;
56
+ interface StartArgs {
57
+ stdio: boolean;
58
+ port?: number;
59
+ host?: string;
60
+ }
61
+ /** 반환 문자열 = 사용법 오류 */
62
+ export declare function parseStartArgs(argv: readonly string[]): StartArgs | string;
63
+ export {};
package/dist/start.js ADDED
@@ -0,0 +1,267 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { readFileSync } from "node:fs";
10
+ import { resolve, sep } from "node:path";
11
+ import { fileURLToPath, pathToFileURL } from "node:url";
12
+ import { COMMAND, isCommand } from "./command.js";
13
+ import { resolveLocalPrincipal } from "./config.js";
14
+ import { loadInstrumentation } from "./instrumentation.js";
15
+ import { MANIFEST_SCHEMA_VERSION } from "./manifest.js";
16
+ import { DEFAULT_HOST, DEFAULT_PORT } from "./mcp/constants.js";
17
+ import { createMcpFetchHandler, startMcpHttpServer } from "./mcp/http.js";
18
+ import { redirectStdout, serveMcpStdio } from "./mcp/stdio.js";
19
+ import { createLazyRegistry } from "./registry.js";
20
+ import { mextVersion } from "./version.js";
21
+ const USAGE = "Usage: mext start [--stdio] [--port <n>] [--host <h>] (node .mext/server.js, PORT · HOST environment variables)";
22
+ /** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */
23
+ export async function startFromBuild(dir, options = {}) {
24
+ const stderr = options.stderr ?? process.stderr;
25
+ const env = options.env ?? process.env;
26
+ const args = parseStartArgs(options.argv ?? []);
27
+ if (typeof args === "string") {
28
+ stderr.write(`error: ${args}\n${USAGE}\n`);
29
+ return 2;
30
+ }
31
+ // stdio: 사용자 코드 import 전부터 그 외 stdout 쓰기를 stderr 로 (mext dev --stdio 와 같음)
32
+ const redirect = args.stdio ? redirectStdout() : undefined;
33
+ const { signal, dispose } = options.signal ? { signal: options.signal, dispose: () => { } } : processSignal();
34
+ try {
35
+ const build = await loadBuild(dir);
36
+ const onerror = (error) => void stderr.write(`mcp: ${error.message}\n`);
37
+ if (args.stdio) {
38
+ const server = serveMcpStdio({
39
+ registry: build.registry,
40
+ instrumentation: build.instrumentation,
41
+ manifestHash: build.manifest.manifestHash,
42
+ principal: await resolveLocalPrincipal(build.config),
43
+ ...(build.config.app ? { app: build.config.app } : {}),
44
+ ...(build.config.tools ? { tools: build.config.tools } : {}),
45
+ stdout: redirect.wire,
46
+ onerror,
47
+ });
48
+ const onAbort = () => void server.close();
49
+ if (signal.aborted)
50
+ onAbort();
51
+ signal.addEventListener("abort", onAbort, { once: true });
52
+ await server.closed;
53
+ return 0;
54
+ }
55
+ const port = args.port ?? envPort(env.PORT) ?? build.config.server?.port ?? DEFAULT_PORT;
56
+ const host = args.host ?? (env.HOST === undefined || env.HOST === "" ? undefined : env.HOST) ?? build.config.server?.host ?? DEFAULT_HOST;
57
+ const http = httpOptions(build, onerror);
58
+ const server = await startMcpHttpServer({ ...http, server: { ...http.server, host, port } });
59
+ stderr.write(`MCP server (Streamable HTTP) listening on ${server.url} (buildId ${build.manifest.buildId})\n`);
60
+ await new Promise((done) => {
61
+ if (signal.aborted)
62
+ return done();
63
+ signal.addEventListener("abort", () => done(), { once: true });
64
+ });
65
+ await server.close();
66
+ return 0;
67
+ }
68
+ catch (error) {
69
+ // 시작 실패 = stdout 원복 (stdio 서버를 띄우기 전)
70
+ redirect?.restore();
71
+ stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\n`);
72
+ return 1;
73
+ }
74
+ finally {
75
+ dispose();
76
+ }
77
+ }
78
+ /**
79
+ * handler.js 의 Web fetch handler ({ fetch(request) → Response }). 동기 반환.
80
+ * D7: 초기화 (manifest 검증 · instrumentation register · config · Registry) = 첫 요청에서 1회 (memoized promise).
81
+ * 초기화 실패 = 그 요청 500 + JSON-RPC 오류 (data.loadError), 다음 요청에서 재시도.
82
+ * MCP 처리 (Origin · auth · 헤더 검증 · SSE progress · MRTR) = node 서버와 같은 createMcpFetchHandler
83
+ */
84
+ export function createFetchHandler(dir, options = {}) {
85
+ const base = dirUrl(dir);
86
+ const onerror = (error) => console.error(`mcp: ${error.message}`);
87
+ let pending;
88
+ const init = () => (pending ??= loadBuild(base, options)
89
+ .then((build) => createMcpFetchHandler(httpOptions(build, onerror)))
90
+ .catch((error) => {
91
+ pending = undefined;
92
+ throw error;
93
+ }));
94
+ return {
95
+ async fetch(request) {
96
+ let handler;
97
+ try {
98
+ handler = await init();
99
+ }
100
+ catch (error) {
101
+ const message = error instanceof Error ? error.message : String(error);
102
+ onerror(error instanceof Error ? error : new Error(message));
103
+ const body = { jsonrpc: "2.0", id: null, error: { code: -32_603, message: "Server failed to start", data: { loadError: message } } };
104
+ return new Response(JSON.stringify(body), { status: 500, headers: { "Content-Type": "application/json" } });
105
+ }
106
+ return handler.fetch(request);
107
+ },
108
+ };
109
+ }
110
+ /** .mext/manifest.json 읽기 + 검증 (없음 · schemaVersion · mextVersion 불일치 = 오류) */
111
+ export function readBuildManifest(dir) {
112
+ const base = dirUrl(dir);
113
+ let text;
114
+ try {
115
+ text = readFileSync(new URL("manifest.json", base), "utf8");
116
+ }
117
+ catch {
118
+ throw new Error(`no build found in ${fileURLToPath(base)} (manifest.json missing). Run "mext build" first`);
119
+ }
120
+ const manifest = JSON.parse(text);
121
+ if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {
122
+ throw new Error(`${fileURLToPath(base)} was built with manifest schemaVersion ${String(manifest.schemaVersion)} (expected ${MANIFEST_SCHEMA_VERSION}). Run "mext build" again`);
123
+ }
124
+ if (manifest.mextVersion !== mextVersion()) {
125
+ throw new Error(`${fileURLToPath(base)} was built with @obov/mext ${manifest.mextVersion}, but ${mextVersion()} is installed. Run "mext build" again`);
126
+ }
127
+ return manifest;
128
+ }
129
+ /** manifest 검증 → instrumentation.js (register, 사용자 코드보다 먼저) → config.js → lazy Registry */
130
+ export async function loadBuild(dir, options = {}) {
131
+ const base = dirUrl(dir);
132
+ const manifest = readBuildManifest(base);
133
+ const instrumentation = await loadInstrumentation(manifest.instrumentation === undefined ? undefined : new URL(manifest.instrumentation, base));
134
+ const config = manifest.config === undefined
135
+ ? {}
136
+ : (await import(__rewriteRelativeImportExtension(new URL(manifest.config, base).href))).default;
137
+ const registry = registryFromManifest(manifest, base, options);
138
+ return { manifest, config, registry, instrumentation };
139
+ }
140
+ /** manifest → lazy Registry. 명령어 모듈 (manifest.commands[].module) 은 load() 에서 import */
141
+ export function registryFromManifest(manifest, dir, options = {}) {
142
+ const base = dirUrl(dir);
143
+ const importModule = options.importModule ?? ((href) => import(__rewriteRelativeImportExtension(href)));
144
+ return createLazyRegistry({
145
+ commands: manifest.commands.map((c) => ({
146
+ id: c.id,
147
+ command: metadataCommand(c),
148
+ aliases: c.aliases,
149
+ ...(c.file !== undefined ? { file: c.file } : {}),
150
+ inputSchema: c.inputSchema,
151
+ ...(c.outputSchema !== undefined ? { outputSchema: c.outputSchema } : {}),
152
+ async load() {
153
+ const href = new URL(c.module, base).href;
154
+ const mod = (await importModule(href));
155
+ if (!isCommand(mod?.default))
156
+ throw new Error(`${c.module}: default export is not a defineCommand result (command ${c.id})`);
157
+ return mod.default;
158
+ },
159
+ })),
160
+ namespaces: [
161
+ ...(manifest.description !== undefined ? [{ id: "", description: manifest.description }] : []),
162
+ ...manifest.namespaces,
163
+ ],
164
+ });
165
+ }
166
+ /**
167
+ * manifest 메타데이터만 있는 Command (RegisteredCommand.command). input · execute 는 모듈 로드 전이라 접근 = 오류
168
+ * (invoke 는 loadCommand 결과를 쓴다)
169
+ */
170
+ function metadataCommand(c) {
171
+ const parent = c.id.includes(".") ? c.id.slice(0, c.id.lastIndexOf(".") + 1) : "";
172
+ const notLoaded = () => {
173
+ throw new Error(`${c.id}: command module is not loaded (use loadCommand)`);
174
+ };
175
+ return Object.freeze({
176
+ [COMMAND]: true,
177
+ description: c.description,
178
+ get input() {
179
+ return notLoaded();
180
+ },
181
+ output: undefined,
182
+ readOnly: c.readOnly,
183
+ destructive: c.destructive,
184
+ requiresConfirmation: c.requiresConfirmation,
185
+ scopes: Object.freeze([...c.scopes]),
186
+ examples: Object.freeze([...c.examples]),
187
+ positional: Object.freeze([...c.positional]),
188
+ aliases: Object.freeze(c.aliases.map((a) => (a.startsWith(parent) ? a.slice(parent.length) : a))),
189
+ execute: notLoaded,
190
+ });
191
+ }
192
+ function httpOptions(build, onerror) {
193
+ const { config } = build;
194
+ return {
195
+ registry: build.registry,
196
+ instrumentation: build.instrumentation,
197
+ manifestHash: build.manifest.manifestHash,
198
+ ...(config.app ? { app: config.app } : {}),
199
+ ...(config.auth ? { auth: config.auth } : {}),
200
+ ...(config.tools ? { tools: config.tools } : {}),
201
+ ...(config.oauth ? { oauth: config.oauth } : {}),
202
+ server: config.server?.allowedOrigins ? { allowedOrigins: config.server.allowedOrigins } : {},
203
+ onerror,
204
+ };
205
+ }
206
+ /** 반환 문자열 = 사용법 오류 */
207
+ export function parseStartArgs(argv) {
208
+ const args = { stdio: false };
209
+ for (let i = 0; i < argv.length; i++) {
210
+ const tok = argv[i];
211
+ if (tok === "--stdio") {
212
+ args.stdio = true;
213
+ continue;
214
+ }
215
+ if (!tok.startsWith("--"))
216
+ return `unexpected argument "${tok}"`;
217
+ const eq = tok.indexOf("=");
218
+ const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq);
219
+ if (name !== "port" && name !== "host")
220
+ return `unknown flag --${name}`;
221
+ const value = eq < 0 ? argv[++i] : tok.slice(eq + 1);
222
+ if (value === undefined || value === "")
223
+ return `--${name} requires a value`;
224
+ if (name === "port") {
225
+ const port = Number(value);
226
+ if (!isPort(port))
227
+ return "--port must be an integer between 0 and 65535";
228
+ args.port = port;
229
+ }
230
+ else
231
+ args.host = value;
232
+ }
233
+ if (args.stdio && (args.port !== undefined || args.host !== undefined))
234
+ return "--port and --host do not apply to --stdio";
235
+ return args;
236
+ }
237
+ function envPort(value) {
238
+ if (value === undefined || value === "")
239
+ return undefined;
240
+ const port = Number(value);
241
+ if (!isPort(port))
242
+ throw new Error(`PORT must be an integer between 0 and 65535 (got "${value}")`);
243
+ return port;
244
+ }
245
+ function isPort(n) {
246
+ return Number.isInteger(n) && n >= 0 && n <= 65_535;
247
+ }
248
+ function dirUrl(dir) {
249
+ if (typeof dir !== "string")
250
+ return dir.href.endsWith("/") ? dir : new URL(`${dir.href}/`);
251
+ const abs = resolve(dir);
252
+ return pathToFileURL(abs.endsWith(sep) ? abs : `${abs}${sep}`);
253
+ }
254
+ function processSignal() {
255
+ const controller = new AbortController();
256
+ const abort = () => controller.abort();
257
+ process.once("SIGINT", abort);
258
+ process.once("SIGTERM", abort);
259
+ return {
260
+ signal: controller.signal,
261
+ dispose() {
262
+ process.off("SIGINT", abort);
263
+ process.off("SIGTERM", abort);
264
+ },
265
+ };
266
+ }
267
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\"\nimport { resolve, sep } from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { COMMAND, isCommand, type Command } from \"./command.ts\"\nimport { resolveLocalPrincipal, type MextConfig } from \"./config.ts\"\nimport { loadInstrumentation, type Instrumentation } from \"./instrumentation.ts\"\nimport { MANIFEST_SCHEMA_VERSION, type BuildManifest, type BuildManifestCommand } from \"./manifest.ts\"\nimport { DEFAULT_HOST, DEFAULT_PORT } from \"./mcp/constants.ts\"\nimport { createMcpFetchHandler, startMcpHttpServer, type McpFetchHandler, type McpHttpOptions } from \"./mcp/http.ts\"\nimport { redirectStdout, serveMcpStdio } from \"./mcp/stdio.ts\"\nimport { createLazyRegistry, type Registry } from \"./registry.ts\"\nimport { mextVersion } from \"./version.ts\"\n\n/**\n * @obov/mext/start: .mext/ (mext build 산출) 실행 런타임. 소스 스캔 · TypeScript 없음.\n * 생성 파일 계약 (mext build 가 쓰는 server.js · handler.js, 바꾸면 기존 빌드가 깨진다):\n * server.js process.exitCode = await startFromBuild(new URL(\".\", import.meta.url), { argv: process.argv.slice(2), env: process.env })\n * handler.js export default createFetchHandler(new URL(\".\", import.meta.url))\n *\n * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.\n * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)\n */\n\nexport interface StartOptions {\n /** server.js · mext start 뒤 인자. --stdio · --port <n> · --host <h> */\n argv?: readonly string[]\n /** PORT · HOST. 기본 process.env */\n env?: Record<string, string | undefined>\n /** abort = 종료. 기본 SIGINT · SIGTERM */\n signal?: AbortSignal\n /** 기본 process.stderr */\n stderr?: { write(chunk: string): unknown }\n}\n\nexport interface FetchHandler {\n /** 거부하지 않는다. 초기화 실패 · 내부 오류 = 500 JSON-RPC 오류 */\n fetch(request: Request): Promise<Response>\n}\n\n/** .mext/ 로드 결과 */\nexport interface LoadedBuild {\n manifest: BuildManifest\n config: MextConfig\n registry: Registry\n instrumentation: Instrumentation\n}\n\nexport interface LoadBuildOptions {\n /** 명령어 모듈 import (테스트 계측). 기본 = 동적 import */\n importModule?: (href: string) => Promise<unknown>\n}\n\nconst USAGE = \"Usage: mext start [--stdio] [--port <n>] [--host <h>] (node .mext/server.js, PORT · HOST environment variables)\"\n\n/** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */\nexport async function startFromBuild(dir: string | URL, options: StartOptions = {}): Promise<number> {\n const stderr = options.stderr ?? process.stderr\n const env = options.env ?? process.env\n const args = parseStartArgs(options.argv ?? [])\n if (typeof args === \"string\") {\n stderr.write(`error: ${args}\\n${USAGE}\\n`)\n return 2\n }\n // stdio: 사용자 코드 import 전부터 그 외 stdout 쓰기를 stderr 로 (mext dev --stdio 와 같음)\n const redirect = args.stdio ? redirectStdout() : undefined\n const { signal, dispose } = options.signal ? { signal: options.signal, dispose: () => {} } : processSignal()\n try {\n const build = await loadBuild(dir)\n const onerror = (error: Error): void => void stderr.write(`mcp: ${error.message}\\n`)\n if (args.stdio) {\n const server = serveMcpStdio({\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n principal: await resolveLocalPrincipal(build.config),\n ...(build.config.app ? { app: build.config.app } : {}),\n ...(build.config.tools ? { tools: build.config.tools } : {}),\n stdout: redirect!.wire,\n onerror,\n })\n const onAbort = (): void => void server.close()\n if (signal.aborted) onAbort()\n signal.addEventListener(\"abort\", onAbort, { once: true })\n await server.closed\n return 0\n }\n const port = args.port ?? envPort(env.PORT) ?? build.config.server?.port ?? DEFAULT_PORT\n const host = args.host ?? (env.HOST === undefined || env.HOST === \"\" ? undefined : env.HOST) ?? build.config.server?.host ?? DEFAULT_HOST\n const http = httpOptions(build, onerror)\n const server = await startMcpHttpServer({ ...http, server: { ...http.server, host, port } })\n stderr.write(`MCP server (Streamable HTTP) listening on ${server.url} (buildId ${build.manifest.buildId})\\n`)\n await new Promise<void>((done) => {\n if (signal.aborted) return done()\n signal.addEventListener(\"abort\", () => done(), { once: true })\n })\n await server.close()\n return 0\n } catch (error) {\n // 시작 실패 = stdout 원복 (stdio 서버를 띄우기 전)\n redirect?.restore()\n stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n } finally {\n dispose()\n }\n}\n\n/**\n * handler.js 의 Web fetch handler ({ fetch(request) → Response }). 동기 반환.\n * D7: 초기화 (manifest 검증 · instrumentation register · config · Registry) = 첫 요청에서 1회 (memoized promise).\n * 초기화 실패 = 그 요청 500 + JSON-RPC 오류 (data.loadError), 다음 요청에서 재시도.\n * MCP 처리 (Origin · auth · 헤더 검증 · SSE progress · MRTR) = node 서버와 같은 createMcpFetchHandler\n */\nexport function createFetchHandler(dir: string | URL, options: LoadBuildOptions = {}): FetchHandler {\n const base = dirUrl(dir)\n const onerror = (error: Error): void => console.error(`mcp: ${error.message}`)\n let pending: Promise<McpFetchHandler> | undefined\n const init = (): Promise<McpFetchHandler> =>\n (pending ??= loadBuild(base, options)\n .then((build) => createMcpFetchHandler(httpOptions(build, onerror)))\n .catch((error: unknown) => {\n pending = undefined\n throw error\n }))\n return {\n async fetch(request) {\n let handler: McpFetchHandler\n try {\n handler = await init()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n onerror(error instanceof Error ? error : new Error(message))\n const body = { jsonrpc: \"2.0\", id: null, error: { code: -32_603, message: \"Server failed to start\", data: { loadError: message } } }\n return new Response(JSON.stringify(body), { status: 500, headers: { \"Content-Type\": \"application/json\" } })\n }\n return handler.fetch(request)\n },\n }\n}\n\n/** .mext/manifest.json 읽기 + 검증 (없음 · schemaVersion · mextVersion 불일치 = 오류) */\nexport function readBuildManifest(dir: string | URL): BuildManifest {\n const base = dirUrl(dir)\n let text: string\n try {\n text = readFileSync(new URL(\"manifest.json\", base), \"utf8\")\n } catch {\n throw new Error(`no build found in ${fileURLToPath(base)} (manifest.json missing). Run \"mext build\" first`)\n }\n const manifest = JSON.parse(text) as BuildManifest\n if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {\n throw new Error(\n `${fileURLToPath(base)} was built with manifest schemaVersion ${String(manifest.schemaVersion)} (expected ${MANIFEST_SCHEMA_VERSION}). Run \"mext build\" again`,\n )\n }\n if (manifest.mextVersion !== mextVersion()) {\n throw new Error(\n `${fileURLToPath(base)} was built with @obov/mext ${manifest.mextVersion}, but ${mextVersion()} is installed. Run \"mext build\" again`,\n )\n }\n return manifest\n}\n\n/** manifest 검증 → instrumentation.js (register, 사용자 코드보다 먼저) → config.js → lazy Registry */\nexport async function loadBuild(dir: string | URL, options: LoadBuildOptions = {}): Promise<LoadedBuild> {\n const base = dirUrl(dir)\n const manifest = readBuildManifest(base)\n const instrumentation = await loadInstrumentation(\n manifest.instrumentation === undefined ? undefined : new URL(manifest.instrumentation, base),\n )\n const config =\n manifest.config === undefined\n ? {}\n : ((await import(new URL(manifest.config, base).href)) as { default: MextConfig }).default\n const registry = registryFromManifest(manifest, base, options)\n return { manifest, config, registry, instrumentation }\n}\n\n/** manifest → lazy Registry. 명령어 모듈 (manifest.commands[].module) 은 load() 에서 import */\nexport function registryFromManifest(manifest: BuildManifest, dir: string | URL, options: LoadBuildOptions = {}): Registry {\n const base = dirUrl(dir)\n const importModule = options.importModule ?? ((href: string) => import(href))\n return createLazyRegistry({\n commands: manifest.commands.map((c) => ({\n id: c.id,\n command: metadataCommand(c),\n aliases: c.aliases,\n ...(c.file !== undefined ? { file: c.file } : {}),\n inputSchema: c.inputSchema,\n ...(c.outputSchema !== undefined ? { outputSchema: c.outputSchema } : {}),\n async load() {\n const href = new URL(c.module, base).href\n const mod = (await importModule(href)) as { default?: unknown } | undefined\n if (!isCommand(mod?.default)) throw new Error(`${c.module}: default export is not a defineCommand result (command ${c.id})`)\n return mod.default\n },\n })),\n namespaces: [\n ...(manifest.description !== undefined ? [{ id: \"\", description: manifest.description }] : []),\n ...manifest.namespaces,\n ],\n })\n}\n\n/**\n * manifest 메타데이터만 있는 Command (RegisteredCommand.command). input · execute 는 모듈 로드 전이라 접근 = 오류\n * (invoke 는 loadCommand 결과를 쓴다)\n */\nfunction metadataCommand(c: BuildManifestCommand): Command {\n const parent = c.id.includes(\".\") ? c.id.slice(0, c.id.lastIndexOf(\".\") + 1) : \"\"\n const notLoaded = (): never => {\n throw new Error(`${c.id}: command module is not loaded (use loadCommand)`)\n }\n return Object.freeze({\n [COMMAND]: true as const,\n description: c.description,\n get input(): never {\n return notLoaded()\n },\n output: undefined,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n scopes: Object.freeze([...c.scopes]),\n examples: Object.freeze([...c.examples]) as Command[\"examples\"],\n positional: Object.freeze([...c.positional]),\n aliases: Object.freeze(c.aliases.map((a) => (a.startsWith(parent) ? a.slice(parent.length) : a))),\n execute: notLoaded,\n })\n}\n\nfunction httpOptions(build: LoadedBuild, onerror: (error: Error) => void): McpHttpOptions {\n const { config } = build\n return {\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n ...(config.app ? { app: config.app } : {}),\n ...(config.auth ? { auth: config.auth } : {}),\n ...(config.tools ? { tools: config.tools } : {}),\n ...(config.oauth ? { oauth: config.oauth } : {}),\n server: config.server?.allowedOrigins ? { allowedOrigins: config.server.allowedOrigins } : {},\n onerror,\n }\n}\n\ninterface StartArgs {\n stdio: boolean\n port?: number\n host?: string\n}\n\n/** 반환 문자열 = 사용법 오류 */\nexport function parseStartArgs(argv: readonly string[]): StartArgs | string {\n const args: StartArgs = { stdio: false }\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!\n if (tok === \"--stdio\") {\n args.stdio = true\n continue\n }\n if (!tok.startsWith(\"--\")) return `unexpected argument \"${tok}\"`\n const eq = tok.indexOf(\"=\")\n const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq)\n if (name !== \"port\" && name !== \"host\") return `unknown flag --${name}`\n const value = eq < 0 ? argv[++i] : tok.slice(eq + 1)\n if (value === undefined || value === \"\") return `--${name} requires a value`\n if (name === \"port\") {\n const port = Number(value)\n if (!isPort(port)) return \"--port must be an integer between 0 and 65535\"\n args.port = port\n } else args.host = value\n }\n if (args.stdio && (args.port !== undefined || args.host !== undefined)) return \"--port and --host do not apply to --stdio\"\n return args\n}\n\nfunction envPort(value: string | undefined): number | undefined {\n if (value === undefined || value === \"\") return undefined\n const port = Number(value)\n if (!isPort(port)) throw new Error(`PORT must be an integer between 0 and 65535 (got \"${value}\")`)\n return port\n}\n\nfunction isPort(n: number): boolean {\n return Number.isInteger(n) && n >= 0 && n <= 65_535\n}\n\nfunction dirUrl(dir: string | URL): URL {\n if (typeof dir !== \"string\") return dir.href.endsWith(\"/\") ? dir : new URL(`${dir.href}/`)\n const abs = resolve(dir)\n return pathToFileURL(abs.endsWith(sep) ? abs : `${abs}${sep}`)\n}\n\nfunction processSignal(): { signal: AbortSignal; dispose(): void } {\n const controller = new AbortController()\n const abort = (): void => controller.abort()\n process.once(\"SIGINT\", abort)\n process.once(\"SIGTERM\", abort)\n return {\n signal: controller.signal,\n dispose() {\n process.off(\"SIGINT\", abort)\n process.off(\"SIGTERM\", abort)\n },\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /** 최적 문자열 정렬 (제한된 Damerau-Levenshtein) 거리 */
2
+ export declare function editDistance(a: string, b: string): number;
3
+ /**
4
+ * 후보 중 query 와 가까운 것 (가까운 순 · 같은 거리는 이름 순).
5
+ * 후보는 [비교할 이름, 돌려줄 값]. 같은 값은 한 번만.
6
+ */
7
+ export declare function closest(query: string, candidates: Iterable<readonly [string, string]>, limit?: number): string[];