@obov/mext 0.2.0 → 0.3.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.
@@ -25,7 +25,7 @@ export interface CustomerCliBuild {
25
25
  /** bin.js 바이트 */
26
26
  size: number;
27
27
  }
28
- /** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp) 과 겹치면 RESERVED_ID */
28
+ /** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp, 로컬 bin = help · mcp) 과 겹치면 RESERVED_ID */
29
29
  export declare function customerCliIssues(cli: CliConfig | undefined, registry: Registry): BuildIssue[];
30
30
  export interface BuildCustomerCliOptions {
31
31
  rolldown: RolldownModule;
@@ -37,3 +37,4 @@ export interface BuildCustomerCliOptions {
37
37
  manifest: BuildManifest;
38
38
  }
39
39
  export declare function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild>;
40
+ export declare function projectVersion(cwd: string): string | undefined;
package/dist/build-cli.js CHANGED
@@ -3,7 +3,7 @@ import { isBuiltin } from "node:module";
3
3
  import { join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { clientManifest } from "./client/manifest.js";
6
- import { CLI_BUILTIN_COMMANDS } from "./config.js";
6
+ import { CLI_BUILTIN_COMMANDS, LOCAL_BIN_BUILTIN_COMMANDS } from "./config.js";
7
7
  import { MextBuildError } from "./errors.js";
8
8
  import { envPrefix } from "./remote/credentials.js";
9
9
  /**
@@ -19,15 +19,16 @@ export const CLI_NODE_ENGINE = ">=22";
19
19
  /** 고객 CLI 런타임 엔트리 (src = .ts, dist = .js: 이 파일과 같은 확장자) */
20
20
  const RUNTIME_ENTRY = fileURLToPath(new URL(`./client/main${import.meta.url.slice(import.meta.url.lastIndexOf("."))}`, import.meta.url));
21
21
  const ENTRY_ID = "\0mext-customer-cli";
22
- /** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp) 과 겹치면 RESERVED_ID */
22
+ /** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp, 로컬 bin = help · mcp) 과 겹치면 RESERVED_ID */
23
23
  export function customerCliIssues(cli, registry) {
24
24
  if (!cli)
25
25
  return [];
26
+ const builtins = cli.local === true ? LOCAL_BIN_BUILTIN_COMMANDS : CLI_BUILTIN_COMMANDS;
26
27
  // 루트 별칭은 Registry 가 금지하므로 루트 명령어 · 네임스페이스만 본다
27
28
  const root = registry.namespaces.get("");
28
29
  const ids = new Set([...(root?.commands ?? []), ...(root?.namespaces ?? [])]);
29
30
  return [...ids]
30
- .filter((id) => CLI_BUILTIN_COMMANDS.includes(id))
31
+ .filter((id) => builtins.includes(id))
31
32
  .sort()
32
33
  .map((id) => {
33
34
  const file = registry.commands.get(id)?.file;
@@ -41,6 +42,9 @@ export function customerCliIssues(cli, registry) {
41
42
  }
42
43
  export async function buildCustomerCli(options) {
43
44
  const { cli, manifest } = options;
45
+ if (cli.url === undefined)
46
+ throw new Error("mext build: customer CLI requires cli.url");
47
+ const url = cli.url;
44
48
  const dir = join(options.outDir, CLI_DIR);
45
49
  mkdirSync(dir, { recursive: true });
46
50
  const version = cli.version ?? projectVersion(options.cwd) ?? "0.0.0";
@@ -48,7 +52,7 @@ export async function buildCustomerCli(options) {
48
52
  const bundled = cli.manifest !== false;
49
53
  const config = {
50
54
  bin: cli.bin,
51
- url: cli.url,
55
+ url,
52
56
  version,
53
57
  packageName,
54
58
  mextVersion: manifest.mextVersion,
@@ -109,14 +113,14 @@ export async function buildCustomerCli(options) {
109
113
  const pkg = {
110
114
  name: packageName,
111
115
  version,
112
- description: `Command-line client for ${cli.url}`,
116
+ description: `Command-line client for ${url}`,
113
117
  type: "module",
114
118
  bin: { [cli.bin]: `./${CLI_BIN_FILE}` },
115
119
  files: [CLI_BIN_FILE],
116
120
  engines: { node: CLI_NODE_ENGINE },
117
121
  };
118
122
  writeFileSync(join(dir, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`);
119
- writeFileSync(join(dir, "README.md"), readme(cli, packageName));
123
+ writeFileSync(join(dir, "README.md"), readme(cli.bin, url, packageName));
120
124
  return {
121
125
  dir,
122
126
  bin: cli.bin,
@@ -127,7 +131,7 @@ export async function buildCustomerCli(options) {
127
131
  size: statSync(binPath).size,
128
132
  };
129
133
  }
130
- function projectVersion(cwd) {
134
+ export function projectVersion(cwd) {
131
135
  try {
132
136
  const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
133
137
  return typeof pkg.version === "string" && pkg.version !== "" ? pkg.version : undefined;
@@ -136,13 +140,12 @@ function projectVersion(cwd) {
136
140
  return undefined;
137
141
  }
138
142
  }
139
- function readme(cli, packageName) {
140
- const { bin } = cli;
143
+ function readme(bin, url, packageName) {
141
144
  const prefix = envPrefix(bin);
142
145
  return [
143
146
  `# ${packageName}`,
144
147
  "",
145
- `Command-line client for ${cli.url}. Generated by mext build.`,
148
+ `Command-line client for ${url}. Generated by mext build.`,
146
149
  "",
147
150
  "```sh",
148
151
  `npm install -g ${packageName}`,
@@ -156,7 +159,7 @@ function readme(cli, packageName) {
156
159
  "Environment:",
157
160
  "",
158
161
  `- \`${prefix}_TOKEN\`: API token (used instead of the stored login)`,
159
- `- \`${prefix}_URL\`: server URL (default: ${cli.url})`,
162
+ `- \`${prefix}_URL\`: server URL (default: ${url})`,
160
163
  `- \`${prefix}_NO_KEYRING=1\`: store the token in \`~/.config/${bin}/credentials.json\` instead of the OS keychain`,
161
164
  `- \`${prefix}_CACHE_DIR\`: server schema cache folder (default: \`\${XDG_CACHE_HOME:-~/.cache}/${bin}\`)`,
162
165
  "",
@@ -1 +1 @@
1
- {"version":3,"file":"build-cli.js","sourceRoot":"","sources":["../src/build-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type { RolldownModule, RolldownOutputItem } from \"./build.ts\"\nimport { clientManifest } from \"./client/manifest.ts\"\nimport type { CustomerCliConfig } from \"./client/main.ts\"\nimport { CLI_BUILTIN_COMMANDS, type CliConfig } from \"./config.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport type { BuildManifest } from \"./manifest.ts\"\nimport type { Registry } from \"./registry.ts\"\nimport { envPrefix } from \"./remote/credentials.ts\"\n\n/**\n * 고객 CLI 패키지 (.mext/cli/, §5). mext build 가 config.cli 가 있을 때 호출.\n * bin.js = client/main.ts (고객 CLI 런타임) + 인라인 설정 (CustomerCliConfig) 의 rolldown 단일 번들. 외부 = Node 내장만\n * (의존성 전부 번들 → package.json dependencies 없음). 런타임은 Zod · MCP 서버 SDK · 핸들러 · 스캔 · 빌드 코드를 import 하지 않는다\n */\n\n/** .mext/ 아래 고객 CLI 폴더 */\nexport const CLI_DIR = \"cli\"\nexport const CLI_BIN_FILE = \"bin.js\"\n/** 고객 CLI package.json engines.node (번들 target node22, 타입 제거 불필요) */\nexport const CLI_NODE_ENGINE = \">=22\"\n\n/** 고객 CLI 런타임 엔트리 (src = .ts, dist = .js: 이 파일과 같은 확장자) */\nconst RUNTIME_ENTRY = fileURLToPath(new URL(`./client/main${import.meta.url.slice(import.meta.url.lastIndexOf(\".\"))}`, import.meta.url))\nconst ENTRY_ID = \"\\0mext-customer-cli\"\n\nexport interface CustomerCliBuild {\n /** 절대 경로 (.mext/cli) */\n dir: string\n bin: string\n packageName: string\n version: string\n form: \"bundled\" | \"universal\"\n /** .mext/cli 기준 생성 파일 (정렬) */\n files: string[]\n /** bin.js 바이트 */\n size: number\n}\n\n/** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp) 과 겹치면 RESERVED_ID */\nexport function customerCliIssues(cli: CliConfig | undefined, registry: Registry): BuildIssue[] {\n if (!cli) return []\n // 루트 별칭은 Registry 가 금지하므로 루트 명령어 · 네임스페이스만 본다\n const root = registry.namespaces.get(\"\")\n const ids = new Set([...(root?.commands ?? []), ...(root?.namespaces ?? [])])\n return [...ids]\n .filter((id) => CLI_BUILTIN_COMMANDS.includes(id))\n .sort()\n .map((id) => {\n const file = registry.commands.get(id)?.file\n return {\n code: \"RESERVED_ID\",\n message: `\"${id}\" is a built-in command of the customer CLI (${cli.bin} ${id}). Rename it or remove cli from mext.config.ts`,\n id,\n ...(file !== undefined ? { file } : {}),\n }\n })\n}\n\nexport interface BuildCustomerCliOptions {\n rolldown: RolldownModule\n /** 도입 프로젝트 루트 (package.json version) */\n cwd: string\n /** .mext/ 절대 경로 */\n outDir: string\n cli: CliConfig\n manifest: BuildManifest\n}\n\nexport async function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild> {\n const { cli, manifest } = options\n const dir = join(options.outDir, CLI_DIR)\n mkdirSync(dir, { recursive: true })\n const version = cli.version ?? projectVersion(options.cwd) ?? \"0.0.0\"\n const packageName = cli.packageName ?? cli.bin\n const bundled = cli.manifest !== false\n const config: CustomerCliConfig = {\n bin: cli.bin,\n url: cli.url,\n version,\n packageName,\n mextVersion: manifest.mextVersion,\n manifestHash: manifest.manifestHash,\n ...(bundled ? { manifest: clientManifest(manifest) } : {}),\n }\n const errorsFile = cli.errors === undefined ? undefined : resolve(options.cwd, cli.errors)\n if (errorsFile !== undefined && !existsSync(errorsFile)) {\n throw new MextBuildError([{ code: \"INVALID_CONFIG\", message: `cli.errors file not found: ${cli.errors}` }])\n }\n const entry = [\n `import { main } from ${JSON.stringify(RUNTIME_ENTRY)}`,\n ...(errorsFile !== undefined ? [`import formatError from ${JSON.stringify(errorsFile)}`] : []),\n \"\",\n errorsFile !== undefined\n ? `await main(${JSON.stringify(config)}, { formatError })`\n : `await main(${JSON.stringify(config)})`,\n \"\",\n ].join(\"\\n\")\n\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await options.rolldown.build({\n cwd: options.cwd,\n input: { bin: ENTRY_ID },\n platform: \"node\",\n external: (id: string): boolean => id.startsWith(\"node:\") || isBuiltin(id),\n plugins: [\n {\n name: \"mext-customer-cli\",\n resolveId: (id: string) => (id === ENTRY_ID ? ENTRY_ID : null),\n load: (id: string) => (id === ENTRY_ID ? entry : null),\n },\n ],\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n output: {\n dir,\n format: \"esm\",\n entryFileNames: CLI_BIN_FILE,\n banner: \"#!/usr/bin/env node\",\n codeSplitting: false,\n sourcemap: false,\n },\n }))\n } catch (error) {\n throw new MextBuildError([\n { code: \"BUNDLE_FAILED\", message: `customer CLI: ${error instanceof Error ? error.message : String(error)}` },\n ])\n }\n const emitted = output.map((o) => o.fileName)\n if (emitted.length !== 1 || emitted[0] !== CLI_BIN_FILE) {\n throw new Error(`mext build: customer CLI bundle must be a single ${CLI_BIN_FILE} (got ${emitted.join(\", \")})`)\n }\n const binPath = join(dir, CLI_BIN_FILE)\n chmodSync(binPath, 0o755)\n\n const pkg = {\n name: packageName,\n version,\n description: `Command-line client for ${cli.url}`,\n type: \"module\",\n bin: { [cli.bin]: `./${CLI_BIN_FILE}` },\n files: [CLI_BIN_FILE],\n engines: { node: CLI_NODE_ENGINE },\n }\n writeFileSync(join(dir, \"package.json\"), `${JSON.stringify(pkg, null, 2)}\\n`)\n writeFileSync(join(dir, \"README.md\"), readme(cli, packageName))\n\n return {\n dir,\n bin: cli.bin,\n packageName,\n version,\n form: bundled ? \"bundled\" : \"universal\",\n files: [CLI_BIN_FILE, \"README.md\", \"package.json\"],\n size: statSync(binPath).size,\n }\n}\n\nfunction projectVersion(cwd: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as { version?: unknown }\n return typeof pkg.version === \"string\" && pkg.version !== \"\" ? pkg.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction readme(cli: CliConfig, packageName: string): string {\n const { bin } = cli\n const prefix = envPrefix(bin)\n return [\n `# ${packageName}`,\n \"\",\n `Command-line client for ${cli.url}. Generated by mext build.`,\n \"\",\n \"```sh\",\n `npm install -g ${packageName}`,\n `${bin} login # log in (browser, device code, or --with-token)`,\n `${bin} help # commands`,\n `${bin} <namespace> <command> --help`,\n `${bin} mcp install # MCP client registration commands`,\n `${bin} logout`,\n \"```\",\n \"\",\n \"Environment:\",\n \"\",\n `- \\`${prefix}_TOKEN\\`: API token (used instead of the stored login)`,\n `- \\`${prefix}_URL\\`: server URL (default: ${cli.url})`,\n `- \\`${prefix}_NO_KEYRING=1\\`: store the token in \\`~/.config/${bin}/credentials.json\\` instead of the OS keychain`,\n `- \\`${prefix}_CACHE_DIR\\`: server schema cache folder (default: \\`\\${XDG_CACHE_HOME:-~/.cache}/${bin}\\`)`,\n \"\",\n \"Exit codes: 0 success · 1 error · 2 invalid arguments or unknown command · 77 authentication or permission · 130 interrupted.\",\n \"\",\n ].join(\"\\n\")\n}\n"]}
1
+ {"version":3,"file":"build-cli.js","sourceRoot":"","sources":["../src/build-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type { RolldownModule, RolldownOutputItem } from \"./build.ts\"\nimport { clientManifest } from \"./client/manifest.ts\"\nimport type { CustomerCliConfig } from \"./client/main.ts\"\nimport { CLI_BUILTIN_COMMANDS, LOCAL_BIN_BUILTIN_COMMANDS, type CliConfig } from \"./config.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport type { BuildManifest } from \"./manifest.ts\"\nimport type { Registry } from \"./registry.ts\"\nimport { envPrefix } from \"./remote/credentials.ts\"\n\n/**\n * 고객 CLI 패키지 (.mext/cli/, §5). mext build 가 config.cli 가 있을 때 호출.\n * bin.js = client/main.ts (고객 CLI 런타임) + 인라인 설정 (CustomerCliConfig) 의 rolldown 단일 번들. 외부 = Node 내장만\n * (의존성 전부 번들 → package.json dependencies 없음). 런타임은 Zod · MCP 서버 SDK · 핸들러 · 스캔 · 빌드 코드를 import 하지 않는다\n */\n\n/** .mext/ 아래 고객 CLI 폴더 */\nexport const CLI_DIR = \"cli\"\nexport const CLI_BIN_FILE = \"bin.js\"\n/** 고객 CLI package.json engines.node (번들 target node22, 타입 제거 불필요) */\nexport const CLI_NODE_ENGINE = \">=22\"\n\n/** 고객 CLI 런타임 엔트리 (src = .ts, dist = .js: 이 파일과 같은 확장자) */\nconst RUNTIME_ENTRY = fileURLToPath(new URL(`./client/main${import.meta.url.slice(import.meta.url.lastIndexOf(\".\"))}`, import.meta.url))\nconst ENTRY_ID = \"\\0mext-customer-cli\"\n\nexport interface CustomerCliBuild {\n /** 절대 경로 (.mext/cli) */\n dir: string\n bin: string\n packageName: string\n version: string\n form: \"bundled\" | \"universal\"\n /** .mext/cli 기준 생성 파일 (정렬) */\n files: string[]\n /** bin.js 바이트 */\n size: number\n}\n\n/** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp, 로컬 bin = help · mcp) 과 겹치면 RESERVED_ID */\nexport function customerCliIssues(cli: CliConfig | undefined, registry: Registry): BuildIssue[] {\n if (!cli) return []\n const builtins = cli.local === true ? LOCAL_BIN_BUILTIN_COMMANDS : CLI_BUILTIN_COMMANDS\n // 루트 별칭은 Registry 가 금지하므로 루트 명령어 · 네임스페이스만 본다\n const root = registry.namespaces.get(\"\")\n const ids = new Set([...(root?.commands ?? []), ...(root?.namespaces ?? [])])\n return [...ids]\n .filter((id) => builtins.includes(id))\n .sort()\n .map((id) => {\n const file = registry.commands.get(id)?.file\n return {\n code: \"RESERVED_ID\",\n message: `\"${id}\" is a built-in command of the customer CLI (${cli.bin} ${id}). Rename it or remove cli from mext.config.ts`,\n id,\n ...(file !== undefined ? { file } : {}),\n }\n })\n}\n\nexport interface BuildCustomerCliOptions {\n rolldown: RolldownModule\n /** 도입 프로젝트 루트 (package.json version) */\n cwd: string\n /** .mext/ 절대 경로 */\n outDir: string\n cli: CliConfig\n manifest: BuildManifest\n}\n\nexport async function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild> {\n const { cli, manifest } = options\n if (cli.url === undefined) throw new Error(\"mext build: customer CLI requires cli.url\")\n const url = cli.url\n const dir = join(options.outDir, CLI_DIR)\n mkdirSync(dir, { recursive: true })\n const version = cli.version ?? projectVersion(options.cwd) ?? \"0.0.0\"\n const packageName = cli.packageName ?? cli.bin\n const bundled = cli.manifest !== false\n const config: CustomerCliConfig = {\n bin: cli.bin,\n url,\n version,\n packageName,\n mextVersion: manifest.mextVersion,\n manifestHash: manifest.manifestHash,\n ...(bundled ? { manifest: clientManifest(manifest) } : {}),\n }\n const errorsFile = cli.errors === undefined ? undefined : resolve(options.cwd, cli.errors)\n if (errorsFile !== undefined && !existsSync(errorsFile)) {\n throw new MextBuildError([{ code: \"INVALID_CONFIG\", message: `cli.errors file not found: ${cli.errors}` }])\n }\n const entry = [\n `import { main } from ${JSON.stringify(RUNTIME_ENTRY)}`,\n ...(errorsFile !== undefined ? [`import formatError from ${JSON.stringify(errorsFile)}`] : []),\n \"\",\n errorsFile !== undefined\n ? `await main(${JSON.stringify(config)}, { formatError })`\n : `await main(${JSON.stringify(config)})`,\n \"\",\n ].join(\"\\n\")\n\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await options.rolldown.build({\n cwd: options.cwd,\n input: { bin: ENTRY_ID },\n platform: \"node\",\n external: (id: string): boolean => id.startsWith(\"node:\") || isBuiltin(id),\n plugins: [\n {\n name: \"mext-customer-cli\",\n resolveId: (id: string) => (id === ENTRY_ID ? ENTRY_ID : null),\n load: (id: string) => (id === ENTRY_ID ? entry : null),\n },\n ],\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n output: {\n dir,\n format: \"esm\",\n entryFileNames: CLI_BIN_FILE,\n banner: \"#!/usr/bin/env node\",\n codeSplitting: false,\n sourcemap: false,\n },\n }))\n } catch (error) {\n throw new MextBuildError([\n { code: \"BUNDLE_FAILED\", message: `customer CLI: ${error instanceof Error ? error.message : String(error)}` },\n ])\n }\n const emitted = output.map((o) => o.fileName)\n if (emitted.length !== 1 || emitted[0] !== CLI_BIN_FILE) {\n throw new Error(`mext build: customer CLI bundle must be a single ${CLI_BIN_FILE} (got ${emitted.join(\", \")})`)\n }\n const binPath = join(dir, CLI_BIN_FILE)\n chmodSync(binPath, 0o755)\n\n const pkg = {\n name: packageName,\n version,\n description: `Command-line client for ${url}`,\n type: \"module\",\n bin: { [cli.bin]: `./${CLI_BIN_FILE}` },\n files: [CLI_BIN_FILE],\n engines: { node: CLI_NODE_ENGINE },\n }\n writeFileSync(join(dir, \"package.json\"), `${JSON.stringify(pkg, null, 2)}\\n`)\n writeFileSync(join(dir, \"README.md\"), readme(cli.bin, url, packageName))\n\n return {\n dir,\n bin: cli.bin,\n packageName,\n version,\n form: bundled ? \"bundled\" : \"universal\",\n files: [CLI_BIN_FILE, \"README.md\", \"package.json\"],\n size: statSync(binPath).size,\n }\n}\n\nexport function projectVersion(cwd: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as { version?: unknown }\n return typeof pkg.version === \"string\" && pkg.version !== \"\" ? pkg.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction readme(bin: string, url: string, packageName: string): string {\n const prefix = envPrefix(bin)\n return [\n `# ${packageName}`,\n \"\",\n `Command-line client for ${url}. Generated by mext build.`,\n \"\",\n \"```sh\",\n `npm install -g ${packageName}`,\n `${bin} login # log in (browser, device code, or --with-token)`,\n `${bin} help # commands`,\n `${bin} <namespace> <command> --help`,\n `${bin} mcp install # MCP client registration commands`,\n `${bin} logout`,\n \"```\",\n \"\",\n \"Environment:\",\n \"\",\n `- \\`${prefix}_TOKEN\\`: API token (used instead of the stored login)`,\n `- \\`${prefix}_URL\\`: server URL (default: ${url})`,\n `- \\`${prefix}_NO_KEYRING=1\\`: store the token in \\`~/.config/${bin}/credentials.json\\` instead of the OS keychain`,\n `- \\`${prefix}_CACHE_DIR\\`: server schema cache folder (default: \\`\\${XDG_CACHE_HOME:-~/.cache}/${bin}\\`)`,\n \"\",\n \"Exit codes: 0 success · 1 error · 2 invalid arguments or unknown command · 77 authentication or permission · 130 interrupted.\",\n \"\",\n ].join(\"\\n\")\n}\n"]}
package/dist/build.d.ts CHANGED
@@ -6,6 +6,9 @@ export declare const BUILD_MANIFEST_FILE = "manifest.json";
6
6
  export declare const INSTRUMENTATION_FILE = "instrumentation.ts";
7
7
  /** 생성 파일 (server.js · handler.js) 이 import 하는 런타임 엔트리 */
8
8
  export declare const START_ENTRY = "@obov/mext/start";
9
+ /** 로컬 bin (cli.local) 실행 파일 · cli.errors 번들 */
10
+ export declare const LOCAL_BIN_FILE = "bin.js";
11
+ export declare const LOCAL_BIN_ERRORS = "cli-errors";
9
12
  /** rolldown 모듈 중 mext build 가 쓰는 부분 */
10
13
  export interface RolldownModule {
11
14
  build(options: Record<string, unknown>): Promise<{
@@ -29,8 +32,11 @@ export interface BuildOptions {
29
32
  importRolldown?: () => Promise<RolldownModule>;
30
33
  }
31
34
  export interface BuildWarning {
32
- /** UNDECLARED_DEPENDENCY = 외부로 남긴 패키지가 package.json dependencies · peerDependencies · optionalDependencies 에 없음 */
33
- code: "UNDECLARED_DEPENDENCY" | "BUNDLER_WARNING";
35
+ /**
36
+ * UNDECLARED_DEPENDENCY = 외부로 남긴 패키지가 package.json dependencies · peerDependencies · optionalDependencies 에 없음.
37
+ * LOCAL_BIN_UNDECLARED = cli.local 인데 package.json bin["<bin>"] 이 .mext/bin.js 가 아님
38
+ */
39
+ code: "UNDECLARED_DEPENDENCY" | "BUNDLER_WARNING" | "LOCAL_BIN_UNDECLARED";
34
40
  message: string;
35
41
  }
36
42
  export interface BuildResult {
@@ -40,8 +46,13 @@ export interface BuildResult {
40
46
  /** outDir 기준 생성 파일 (posix, 정렬) */
41
47
  files: string[];
42
48
  warnings: BuildWarning[];
43
- /** 고객 CLI (.mext/cli/). config.cli 가 있을 때 */
49
+ /** 고객 CLI (.mext/cli/). config.cli 가 있고 local 이 아닐 때 */
44
50
  cli?: CustomerCliBuild;
51
+ /** 로컬 bin (.mext/bin.js). config.cli.local 일 때 */
52
+ localBin?: {
53
+ bin: string;
54
+ version: string;
55
+ };
45
56
  }
46
57
  /** rolldown 을 찾을 수 없음. message 에 설치 명령 */
47
58
  export declare class RolldownUnavailableError extends Error {
@@ -56,6 +67,15 @@ export declare class RolldownUnavailableError extends Error {
56
67
  * 4. server.js · handler.js (얇은 생성 파일, 런타임 = @obov/mext/start) · package.json (type module) · manifest.json (마지막)
57
68
  */
58
69
  export declare function buildProject(options?: BuildOptions): Promise<BuildResult>;
70
+ /**
71
+ * .mext/bin.js (로컬 bin, cli.local). 계약: mainLocalBin(.mext 폴더 URL, { bin, version, errors? }).
72
+ * 도입 프로젝트 package.json bin 이 이 파일을 가리킨다 (npm 이 symlink 해도 import.meta.url = 실제 경로)
73
+ */
74
+ export declare function localBinJs(options: {
75
+ bin: string;
76
+ version: string;
77
+ errors?: string;
78
+ }): string;
59
79
  /**
60
80
  * .mext/server.js. 계약: startFromBuild(.mext 폴더 URL, { argv, env }) → exit code.
61
81
  * argv = server.js 뒤 인자 (--stdio), env = PORT · HOST
package/dist/build.js CHANGED
@@ -7,10 +7,10 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  import { createHash } from "node:crypto";
10
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
11
11
  import { isBuiltin } from "node:module";
12
12
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
13
- import { buildCustomerCli, CLI_DIR, customerCliIssues } from "./build-cli.js";
13
+ import { buildCustomerCli, CLI_DIR, customerCliIssues, projectVersion } from "./build-cli.js";
14
14
  import { cliBuildIssues } from "./cli/args.js";
15
15
  import { CONFIG_FILE, loadConfig, resolveCommandsDir } from "./config.js";
16
16
  import { MextBuildError } from "./errors.js";
@@ -24,6 +24,9 @@ export const BUILD_MANIFEST_FILE = "manifest.json";
24
24
  export const INSTRUMENTATION_FILE = "instrumentation.ts";
25
25
  /** 생성 파일 (server.js · handler.js) 이 import 하는 런타임 엔트리 */
26
26
  export const START_ENTRY = "@obov/mext/start";
27
+ /** 로컬 bin (cli.local) 실행 파일 · cli.errors 번들 */
28
+ export const LOCAL_BIN_FILE = "bin.js";
29
+ export const LOCAL_BIN_ERRORS = "cli-errors";
27
30
  /** 빌드가 지우지 않는 .mext/ 하위 (mext dev 산출 · 잠금) */
28
31
  const PRESERVED_ENTRIES = new Set(["dev"]);
29
32
  /** rolldown 을 찾을 수 없음. message 에 설치 명령 */
@@ -77,6 +80,13 @@ export async function buildProject(options = {}) {
77
80
  const hasInstrumentation = existsSync(join(cwd, INSTRUMENTATION_FILE));
78
81
  if (hasInstrumentation)
79
82
  input.instrumentation = join(cwd, INSTRUMENTATION_FILE);
83
+ const local = config.cli?.local === true ? config.cli : undefined;
84
+ if (local?.errors !== undefined) {
85
+ const errorsFile = resolve(cwd, local.errors);
86
+ if (!existsSync(errorsFile))
87
+ throw new MextBuildError([{ code: "INVALID_CONFIG", message: `cli.errors file not found: ${local.errors}` }]);
88
+ input[LOCAL_BIN_ERRORS] = errorsFile;
89
+ }
80
90
  cleanOutDir(outDir);
81
91
  const warnings = [];
82
92
  /** 외부 패키지 이름 → 처음 import 한 파일 */
@@ -148,10 +158,24 @@ export async function buildProject(options = {}) {
148
158
  "server.js": SERVER_JS,
149
159
  "handler.js": HANDLER_JS,
150
160
  };
161
+ const localBin = local ? { bin: local.bin, version: local.version ?? projectVersion(cwd) ?? "0.0.0" } : undefined;
162
+ if (localBin) {
163
+ generated[LOCAL_BIN_FILE] = localBinJs({ ...localBin, ...(local?.errors !== undefined ? { errors: `${LOCAL_BIN_ERRORS}.js` } : {}) });
164
+ const declaredBin = packageBin(cwd, localBin.bin);
165
+ const expected = `${toPosix(relative(cwd, outDir))}/${LOCAL_BIN_FILE}`;
166
+ if (declaredBin === undefined || declaredBin.replace(/^\.\//, "") !== expected) {
167
+ warnings.push({
168
+ code: "LOCAL_BIN_UNDECLARED",
169
+ message: `cli.local: add "bin": { "${localBin.bin}": "${expected}" } to package.json (and include ${toPosix(relative(cwd, outDir))} in "files")`,
170
+ });
171
+ }
172
+ }
151
173
  for (const [file, content] of Object.entries(generated))
152
174
  writeFileSync(join(outDir, file), content);
153
- // 고객 CLI (.mext/cli/, §5): manifest · buildId 확정 후, manifest.json 쓰기 전
154
- const cli = config.cli ? await buildCustomerCli({ rolldown, cwd, outDir, cli: config.cli, manifest }) : undefined;
175
+ if (localBin)
176
+ chmodSync(join(outDir, LOCAL_BIN_FILE), 0o755);
177
+ // 고객 CLI (.mext/cli/, §5): manifest · buildId 확정 후, manifest.json 쓰기 전. 로컬 bin 이면 만들지 않는다
178
+ const cli = config.cli && !local ? await buildCustomerCli({ rolldown, cwd, outDir, cli: config.cli, manifest }) : undefined;
155
179
  // manifest.json 은 마지막: 중간 실패한 빌드는 manifest 가 없어 mext start 가 "mext build 먼저" 로 거부
156
180
  writeFileSync(join(outDir, BUILD_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\n`);
157
181
  const files = [
@@ -160,7 +184,36 @@ export async function buildProject(options = {}) {
160
184
  BUILD_MANIFEST_FILE,
161
185
  ...(cli?.files.map((f) => `${CLI_DIR}/${f}`) ?? []),
162
186
  ].sort();
163
- return { outDir, manifest, files, warnings, ...(cli ? { cli } : {}) };
187
+ return { outDir, manifest, files, warnings, ...(cli ? { cli } : {}), ...(localBin ? { localBin } : {}) };
188
+ }
189
+ /**
190
+ * .mext/bin.js (로컬 bin, cli.local). 계약: mainLocalBin(.mext 폴더 URL, { bin, version, errors? }).
191
+ * 도입 프로젝트 package.json bin 이 이 파일을 가리킨다 (npm 이 symlink 해도 import.meta.url = 실제 경로)
192
+ */
193
+ export function localBinJs(options) {
194
+ return `#!/usr/bin/env node
195
+ // Generated by mext build. Do not edit.
196
+ // Local bin (cli.local): ${options.bin} help · ${options.bin} <namespace> <command> · ${options.bin} mcp serve
197
+ import { mainLocalBin } from "${START_ENTRY}"
198
+
199
+ await mainLocalBin(new URL(".", import.meta.url), ${JSON.stringify(options)})
200
+ `;
201
+ }
202
+ /** package.json bin[name] (문자열 bin = 패키지 이름이 name 일 때) */
203
+ function packageBin(cwd, name) {
204
+ try {
205
+ const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
206
+ if (typeof pkg.bin === "string")
207
+ return pkg.name === name ? pkg.bin : undefined;
208
+ if (typeof pkg.bin === "object" && pkg.bin !== null) {
209
+ const v = pkg.bin[name];
210
+ return typeof v === "string" ? v : undefined;
211
+ }
212
+ }
213
+ catch {
214
+ // package.json 없음 · 형식 오류 = 미선언
215
+ }
216
+ return undefined;
164
217
  }
165
218
  /**
166
219
  * .mext/server.js. 계약: startFromBuild(.mext 폴더 URL, { argv, env }) → exit code.
package/dist/build.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\"\nimport { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\"\nimport { buildCustomerCli, CLI_DIR, customerCliIssues, type CustomerCliBuild } from \"./build-cli.ts\"\nimport { cliBuildIssues } from \"./cli/args.ts\"\nimport { CONFIG_FILE, loadConfig, resolveCommandsDir } from \"./config.ts\"\nimport { MextBuildError } from \"./errors.ts\"\nimport { buildManifest, manifestHash, MANIFEST_SCHEMA_VERSION, type BuildManifest } from \"./manifest.ts\"\nimport { collectRegistry } from \"./registry.ts\"\nimport { collectCommands } from \"./scan.ts\"\nimport { mextVersion } from \"./version.ts\"\n\n/** mext build 산출 폴더 (도입 프로젝트 루트 기준) */\nexport const BUILD_DIR = \".mext\"\nexport const BUILD_MANIFEST_FILE = \"manifest.json\"\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n/** 생성 파일 (server.js · handler.js) 이 import 하는 런타임 엔트리 */\nexport const START_ENTRY = \"@obov/mext/start\"\n/** 빌드가 지우지 않는 .mext/ 하위 (mext dev 산출 · 잠금) */\nconst PRESERVED_ENTRIES = new Set([\"dev\"])\n\n/** rolldown 모듈 중 mext build 가 쓰는 부분 */\nexport interface RolldownModule {\n build(options: Record<string, unknown>): Promise<{ output: readonly RolldownOutputItem[] }>\n}\n\nexport interface RolldownOutputItem {\n type: \"chunk\" | \"asset\"\n fileName: string\n /** chunk 코드 */\n code?: string\n isEntry?: boolean\n name?: string\n}\n\nexport interface BuildOptions {\n /** 도입 프로젝트 루트 (mext.config.ts · package.json · instrumentation.ts 위치). 기본 process.cwd() */\n cwd?: string\n /** 산출 폴더. 기본 <cwd>/.mext. 기존 내용은 dev/ 외 삭제 */\n outDir?: string\n /** rolldown 로더 (테스트 주입). 기본 import(\"rolldown\") (선택 peer dependency) */\n importRolldown?: () => Promise<RolldownModule>\n}\n\nexport interface BuildWarning {\n /** UNDECLARED_DEPENDENCY = 외부로 남긴 패키지가 package.json dependencies · peerDependencies · optionalDependencies 에 없음 */\n code: \"UNDECLARED_DEPENDENCY\" | \"BUNDLER_WARNING\"\n message: string\n}\n\nexport interface BuildResult {\n /** 산출 폴더 절대 경로 */\n outDir: string\n manifest: BuildManifest\n /** outDir 기준 생성 파일 (posix, 정렬) */\n files: string[]\n warnings: BuildWarning[]\n /** 고객 CLI (.mext/cli/). config.cli 가 있을 때 */\n cli?: CustomerCliBuild\n}\n\n/** rolldown 을 찾을 수 없음. message 에 설치 명령 */\nexport class RolldownUnavailableError extends Error {\n readonly installCommand: string\n\n constructor(installCommand: string, cause?: unknown) {\n super(`mext build requires rolldown (optional peer dependency of @obov/mext). Install it: ${installCommand}`, { cause })\n this.name = \"RolldownUnavailableError\"\n this.installCommand = installCommand\n }\n}\n\n/**\n * mext build: commands/ → .mext/ (§10).\n * 1. rolldown 확인 (없으면 RolldownUnavailableError)\n * 2. 검증: 파일 규칙 · import · Registry (ID · 별칭 · 스키마) · CLI 변환 issue 를 모두 모아 MextBuildError 한 번\n * 3. rolldown 다중 엔트리 번들: commands/**\\/*.js (명령어별) · config.js · instrumentation.js · chunks/. 패키지 import 는 외부\n * 4. server.js · handler.js (얇은 생성 파일, 런타임 = @obov/mext/start) · package.json (type module) · manifest.json (마지막)\n */\nexport async function buildProject(options: BuildOptions = {}): Promise<BuildResult> {\n const cwd = resolve(options.cwd ?? process.cwd())\n const outDir = resolve(cwd, options.outDir ?? BUILD_DIR)\n // 빌드 전 outDir 을 비우므로 프로젝트 폴더 자체 · 상위는 거부\n const up = relative(outDir, cwd)\n if (up === \"\" || (!up.startsWith(\"..\") && !isAbsolute(up))) {\n throw new Error(`mext build: outDir must not be the project directory or its parent (${outDir})`)\n }\n const rolldown = await (options.importRolldown ?? importRolldown)().catch((error: unknown) => {\n if (isModuleNotFound(error, \"rolldown\")) throw new RolldownUnavailableError(installCommand(cwd), error)\n throw error\n })\n\n const config = await loadConfig(cwd)\n const commandsDir = resolveCommandsDir(cwd, config)\n const scanned = await collectCommands(commandsDir)\n const { registry, issues: registryIssues } = collectRegistry(scanned)\n const issues = [...scanned.issues, ...registryIssues, ...cliBuildIssues(registry), ...customerCliIssues(config.cli, registry)]\n if (issues.length > 0) throw new MextBuildError(issues)\n\n // 엔트리 이름 = 출력 경로 (확장자 제외). 명령어 = commands/ 아래 소스 경로 그대로\n const input: Record<string, string> = {}\n const moduleOf = new Map<string, string>()\n for (const entry of registry.commands.values()) {\n const file = entry.file!\n const name = `commands/${file.replace(/\\.ts$/, \"\")}`\n input[name] = join(commandsDir, file)\n moduleOf.set(entry.id, `${name}.js`)\n }\n const hasConfig = existsSync(join(cwd, CONFIG_FILE))\n if (hasConfig) input.config = join(cwd, CONFIG_FILE)\n const hasInstrumentation = existsSync(join(cwd, INSTRUMENTATION_FILE))\n if (hasInstrumentation) input.instrumentation = join(cwd, INSTRUMENTATION_FILE)\n\n cleanOutDir(outDir)\n\n const warnings: BuildWarning[] = []\n /** 외부 패키지 이름 → 처음 import 한 파일 */\n const externals = new Map<string, string | undefined>()\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await rolldown.build({\n cwd,\n input,\n platform: \"node\",\n // 상대 · 절대 경로 · # (package.json imports) = 번들. 그 외 bare import = 외부 (도입 프로젝트 node_modules 에서 해석)\n external: (id: string, importer: string | undefined): boolean => {\n if (id.startsWith(\".\") || id.startsWith(\"#\") || id.startsWith(\"\\0\") || isAbsolute(id)) return false\n if (id.startsWith(\"node:\") || isBuiltin(id)) return true\n const pkg = packageName(id)\n if (!externals.has(pkg) && importer !== undefined && !importer.startsWith(\"\\0\")) externals.set(pkg, importer)\n return true\n },\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n onLog: (_level: string, log: { message: string }) => warnings.push({ code: \"BUNDLER_WARNING\", message: log.message }),\n output: {\n dir: outDir,\n format: \"esm\",\n entryFileNames: \"[name].js\",\n chunkFileNames: \"chunks/[name]-[hash].js\",\n sourcemap: true,\n },\n }))\n } catch (error) {\n throw new MextBuildError([{ code: \"BUNDLE_FAILED\", message: error instanceof Error ? error.message : String(error) }])\n }\n\n const declared = declaredDependencies(cwd)\n for (const [pkg, importer] of externals) {\n if (declared.has(pkg)) continue\n const from = importer === undefined ? \"\" : ` (imported by ${toPosix(relative(cwd, importer))})`\n warnings.push({\n code: \"UNDECLARED_DEPENDENCY\",\n message: `\"${pkg}\" is not in package.json dependencies${from}. It stays external and must be installed where .mext/ runs`,\n })\n }\n\n const emitted = new Set(output.map((o) => o.fileName))\n for (const name of Object.keys(input)) {\n if (!emitted.has(`${name}.js`)) throw new Error(`mext build: rolldown did not emit ${name}.js`)\n }\n\n const base = buildManifest(registry)\n const hash = manifestHash(base.commands)\n const version = mextVersion()\n const manifest: BuildManifest = {\n schemaVersion: MANIFEST_SCHEMA_VERSION,\n mextVersion: version,\n buildId: buildIdOf(version, hash, output),\n manifestHash: hash,\n ...(hasConfig ? { config: \"config.js\" } : {}),\n ...(hasInstrumentation ? { instrumentation: \"instrumentation.js\" } : {}),\n ...(base.description !== undefined ? { description: base.description } : {}),\n namespaces: base.namespaces,\n commands: base.commands.map((c) => ({ ...c, module: moduleOf.get(c.id)! })),\n }\n\n const generated: Record<string, string> = {\n \"package.json\": `${JSON.stringify({ type: \"module\" }, null, 2)}\\n`,\n \"server.js\": SERVER_JS,\n \"handler.js\": HANDLER_JS,\n }\n for (const [file, content] of Object.entries(generated)) writeFileSync(join(outDir, file), content)\n\n // 고객 CLI (.mext/cli/, §5): manifest · buildId 확정 후, manifest.json 쓰기 전\n const cli = config.cli ? await buildCustomerCli({ rolldown, cwd, outDir, cli: config.cli, manifest }) : undefined\n\n // manifest.json 은 마지막: 중간 실패한 빌드는 manifest 가 없어 mext start 가 \"mext build 먼저\" 로 거부\n writeFileSync(join(outDir, BUILD_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`)\n\n const files = [\n ...emitted,\n ...Object.keys(generated),\n BUILD_MANIFEST_FILE,\n ...(cli?.files.map((f) => `${CLI_DIR}/${f}`) ?? []),\n ].sort()\n return { outDir, manifest, files, warnings, ...(cli ? { cli } : {}) }\n}\n\n/**\n * .mext/server.js. 계약: startFromBuild(.mext 폴더 URL, { argv, env }) → exit code.\n * argv = server.js 뒤 인자 (--stdio), env = PORT · HOST\n */\nexport const SERVER_JS = `// Generated by mext build. Do not edit.\n// node .mext/server.js [--stdio] (= mext start). Environment: PORT · HOST\nimport { startFromBuild } from \"${START_ENTRY}\"\n\nprocess.exitCode = await startFromBuild(new URL(\".\", import.meta.url), { argv: process.argv.slice(2), env: process.env })\n`\n\n/** .mext/handler.js. 계약: createFetchHandler(.mext 폴더 URL) → { fetch(request: Request): Promise<Response> } (동기 반환) */\nexport const HANDLER_JS = `// Generated by mext build. Do not edit.\n// Web fetch handler (Request → Response) for other runtimes and platforms\nimport { createFetchHandler } from \"${START_ENTRY}\"\n\nexport default createFetchHandler(new URL(\".\", import.meta.url))\n`\n\nasync function importRolldown(): Promise<RolldownModule> {\n // 문자열 변수: 타입 검사 · 번들러가 선택 peer 를 정적 의존으로 취급하지 않게\n const specifier = \"rolldown\"\n return (await import(specifier)) as RolldownModule\n}\n\nfunction isModuleNotFound(error: unknown, pkg: string): boolean {\n const e = error as { code?: unknown; message?: unknown } | null\n if (typeof e !== \"object\" || e === null) return false\n if (e.code !== \"ERR_MODULE_NOT_FOUND\" && e.code !== \"MODULE_NOT_FOUND\") return false\n return typeof e.message !== \"string\" || e.message.includes(`'${pkg}'`) || e.message.includes(`\"${pkg}\"`)\n}\n\n/** 도입 프로젝트의 lock 파일로 패키지 관리자 추정 */\nfunction installCommand(cwd: string): string {\n for (let dir = cwd; ; dir = resolve(dir, \"..\")) {\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) return \"pnpm add -D rolldown\"\n if (existsSync(join(dir, \"yarn.lock\"))) return \"yarn add -D rolldown\"\n if (existsSync(join(dir, \"bun.lock\")) || existsSync(join(dir, \"bun.lockb\"))) return \"bun add -d rolldown\"\n if (existsSync(join(dir, \"package-lock.json\")) || resolve(dir, \"..\") === dir) return \"npm install --save-dev rolldown\"\n }\n}\n\nfunction cleanOutDir(outDir: string): void {\n mkdirSync(outDir, { recursive: true })\n for (const name of readdirSync(outDir)) {\n if (!PRESERVED_ENTRIES.has(name)) rmSync(join(outDir, name), { recursive: true, force: true })\n }\n}\n\n/** \"@scope/pkg/sub\" → \"@scope/pkg\" · \"pkg/sub\" → \"pkg\" */\nfunction packageName(specifier: string): string {\n const parts = specifier.split(\"/\")\n return specifier.startsWith(\"@\") ? parts.slice(0, 2).join(\"/\") : parts[0]!\n}\n\nfunction declaredDependencies(cwd: string): Set<string> {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as Record<string, unknown>\n } catch {\n return new Set()\n }\n const names = new Set<string>()\n if (typeof pkg.name === \"string\") names.add(pkg.name)\n for (const field of [\"dependencies\", \"peerDependencies\", \"optionalDependencies\"]) {\n const deps = pkg[field]\n if (typeof deps === \"object\" && deps !== null) for (const name of Object.keys(deps)) names.add(name)\n }\n return names\n}\n\n/** D3: mextVersion · manifestHash · 번들 chunk (파일 이름 · 코드) 의 sha256 앞 16자. 같은 입력 = 같은 buildId */\nfunction buildIdOf(version: string, hash: string, output: readonly RolldownOutputItem[]): string {\n const h = createHash(\"sha256\").update(`${version}\\n${hash}\\n`)\n for (const item of [...output].sort((a, b) => (a.fileName < b.fileName ? -1 : 1))) {\n if (item.type !== \"chunk\") continue\n h.update(`${item.fileName}\\0${item.code ?? \"\"}\\0`)\n }\n return h.digest(\"hex\").slice(0, 16)\n}\n\nfunction toPosix(path: string): string {\n return path.split(sep).join(\"/\")\n}\n"]}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\"\nimport { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\"\nimport { buildCustomerCli, CLI_DIR, customerCliIssues, projectVersion, type CustomerCliBuild } from \"./build-cli.ts\"\nimport { cliBuildIssues } from \"./cli/args.ts\"\nimport { CONFIG_FILE, loadConfig, resolveCommandsDir } from \"./config.ts\"\nimport { MextBuildError } from \"./errors.ts\"\nimport { buildManifest, manifestHash, MANIFEST_SCHEMA_VERSION, type BuildManifest } from \"./manifest.ts\"\nimport { collectRegistry } from \"./registry.ts\"\nimport { collectCommands } from \"./scan.ts\"\nimport { mextVersion } from \"./version.ts\"\n\n/** mext build 산출 폴더 (도입 프로젝트 루트 기준) */\nexport const BUILD_DIR = \".mext\"\nexport const BUILD_MANIFEST_FILE = \"manifest.json\"\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n/** 생성 파일 (server.js · handler.js) 이 import 하는 런타임 엔트리 */\nexport const START_ENTRY = \"@obov/mext/start\"\n/** 로컬 bin (cli.local) 실행 파일 · cli.errors 번들 */\nexport const LOCAL_BIN_FILE = \"bin.js\"\nexport const LOCAL_BIN_ERRORS = \"cli-errors\"\n/** 빌드가 지우지 않는 .mext/ 하위 (mext dev 산출 · 잠금) */\nconst PRESERVED_ENTRIES = new Set([\"dev\"])\n\n/** rolldown 모듈 중 mext build 가 쓰는 부분 */\nexport interface RolldownModule {\n build(options: Record<string, unknown>): Promise<{ output: readonly RolldownOutputItem[] }>\n}\n\nexport interface RolldownOutputItem {\n type: \"chunk\" | \"asset\"\n fileName: string\n /** chunk 코드 */\n code?: string\n isEntry?: boolean\n name?: string\n}\n\nexport interface BuildOptions {\n /** 도입 프로젝트 루트 (mext.config.ts · package.json · instrumentation.ts 위치). 기본 process.cwd() */\n cwd?: string\n /** 산출 폴더. 기본 <cwd>/.mext. 기존 내용은 dev/ 외 삭제 */\n outDir?: string\n /** rolldown 로더 (테스트 주입). 기본 import(\"rolldown\") (선택 peer dependency) */\n importRolldown?: () => Promise<RolldownModule>\n}\n\nexport interface BuildWarning {\n /**\n * UNDECLARED_DEPENDENCY = 외부로 남긴 패키지가 package.json dependencies · peerDependencies · optionalDependencies 에 없음.\n * LOCAL_BIN_UNDECLARED = cli.local 인데 package.json bin[\"<bin>\"] 이 .mext/bin.js 가 아님\n */\n code: \"UNDECLARED_DEPENDENCY\" | \"BUNDLER_WARNING\" | \"LOCAL_BIN_UNDECLARED\"\n message: string\n}\n\nexport interface BuildResult {\n /** 산출 폴더 절대 경로 */\n outDir: string\n manifest: BuildManifest\n /** outDir 기준 생성 파일 (posix, 정렬) */\n files: string[]\n warnings: BuildWarning[]\n /** 고객 CLI (.mext/cli/). config.cli 가 있고 local 이 아닐 때 */\n cli?: CustomerCliBuild\n /** 로컬 bin (.mext/bin.js). config.cli.local 일 때 */\n localBin?: { bin: string; version: string }\n}\n\n/** rolldown 을 찾을 수 없음. message 에 설치 명령 */\nexport class RolldownUnavailableError extends Error {\n readonly installCommand: string\n\n constructor(installCommand: string, cause?: unknown) {\n super(`mext build requires rolldown (optional peer dependency of @obov/mext). Install it: ${installCommand}`, { cause })\n this.name = \"RolldownUnavailableError\"\n this.installCommand = installCommand\n }\n}\n\n/**\n * mext build: commands/ → .mext/ (§10).\n * 1. rolldown 확인 (없으면 RolldownUnavailableError)\n * 2. 검증: 파일 규칙 · import · Registry (ID · 별칭 · 스키마) · CLI 변환 issue 를 모두 모아 MextBuildError 한 번\n * 3. rolldown 다중 엔트리 번들: commands/**\\/*.js (명령어별) · config.js · instrumentation.js · chunks/. 패키지 import 는 외부\n * 4. server.js · handler.js (얇은 생성 파일, 런타임 = @obov/mext/start) · package.json (type module) · manifest.json (마지막)\n */\nexport async function buildProject(options: BuildOptions = {}): Promise<BuildResult> {\n const cwd = resolve(options.cwd ?? process.cwd())\n const outDir = resolve(cwd, options.outDir ?? BUILD_DIR)\n // 빌드 전 outDir 을 비우므로 프로젝트 폴더 자체 · 상위는 거부\n const up = relative(outDir, cwd)\n if (up === \"\" || (!up.startsWith(\"..\") && !isAbsolute(up))) {\n throw new Error(`mext build: outDir must not be the project directory or its parent (${outDir})`)\n }\n const rolldown = await (options.importRolldown ?? importRolldown)().catch((error: unknown) => {\n if (isModuleNotFound(error, \"rolldown\")) throw new RolldownUnavailableError(installCommand(cwd), error)\n throw error\n })\n\n const config = await loadConfig(cwd)\n const commandsDir = resolveCommandsDir(cwd, config)\n const scanned = await collectCommands(commandsDir)\n const { registry, issues: registryIssues } = collectRegistry(scanned)\n const issues = [...scanned.issues, ...registryIssues, ...cliBuildIssues(registry), ...customerCliIssues(config.cli, registry)]\n if (issues.length > 0) throw new MextBuildError(issues)\n\n // 엔트리 이름 = 출력 경로 (확장자 제외). 명령어 = commands/ 아래 소스 경로 그대로\n const input: Record<string, string> = {}\n const moduleOf = new Map<string, string>()\n for (const entry of registry.commands.values()) {\n const file = entry.file!\n const name = `commands/${file.replace(/\\.ts$/, \"\")}`\n input[name] = join(commandsDir, file)\n moduleOf.set(entry.id, `${name}.js`)\n }\n const hasConfig = existsSync(join(cwd, CONFIG_FILE))\n if (hasConfig) input.config = join(cwd, CONFIG_FILE)\n const hasInstrumentation = existsSync(join(cwd, INSTRUMENTATION_FILE))\n if (hasInstrumentation) input.instrumentation = join(cwd, INSTRUMENTATION_FILE)\n const local = config.cli?.local === true ? config.cli : undefined\n if (local?.errors !== undefined) {\n const errorsFile = resolve(cwd, local.errors)\n if (!existsSync(errorsFile)) throw new MextBuildError([{ code: \"INVALID_CONFIG\", message: `cli.errors file not found: ${local.errors}` }])\n input[LOCAL_BIN_ERRORS] = errorsFile\n }\n\n cleanOutDir(outDir)\n\n const warnings: BuildWarning[] = []\n /** 외부 패키지 이름 → 처음 import 한 파일 */\n const externals = new Map<string, string | undefined>()\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await rolldown.build({\n cwd,\n input,\n platform: \"node\",\n // 상대 · 절대 경로 · # (package.json imports) = 번들. 그 외 bare import = 외부 (도입 프로젝트 node_modules 에서 해석)\n external: (id: string, importer: string | undefined): boolean => {\n if (id.startsWith(\".\") || id.startsWith(\"#\") || id.startsWith(\"\\0\") || isAbsolute(id)) return false\n if (id.startsWith(\"node:\") || isBuiltin(id)) return true\n const pkg = packageName(id)\n if (!externals.has(pkg) && importer !== undefined && !importer.startsWith(\"\\0\")) externals.set(pkg, importer)\n return true\n },\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n onLog: (_level: string, log: { message: string }) => warnings.push({ code: \"BUNDLER_WARNING\", message: log.message }),\n output: {\n dir: outDir,\n format: \"esm\",\n entryFileNames: \"[name].js\",\n chunkFileNames: \"chunks/[name]-[hash].js\",\n sourcemap: true,\n },\n }))\n } catch (error) {\n throw new MextBuildError([{ code: \"BUNDLE_FAILED\", message: error instanceof Error ? error.message : String(error) }])\n }\n\n const declared = declaredDependencies(cwd)\n for (const [pkg, importer] of externals) {\n if (declared.has(pkg)) continue\n const from = importer === undefined ? \"\" : ` (imported by ${toPosix(relative(cwd, importer))})`\n warnings.push({\n code: \"UNDECLARED_DEPENDENCY\",\n message: `\"${pkg}\" is not in package.json dependencies${from}. It stays external and must be installed where .mext/ runs`,\n })\n }\n\n const emitted = new Set(output.map((o) => o.fileName))\n for (const name of Object.keys(input)) {\n if (!emitted.has(`${name}.js`)) throw new Error(`mext build: rolldown did not emit ${name}.js`)\n }\n\n const base = buildManifest(registry)\n const hash = manifestHash(base.commands)\n const version = mextVersion()\n const manifest: BuildManifest = {\n schemaVersion: MANIFEST_SCHEMA_VERSION,\n mextVersion: version,\n buildId: buildIdOf(version, hash, output),\n manifestHash: hash,\n ...(hasConfig ? { config: \"config.js\" } : {}),\n ...(hasInstrumentation ? { instrumentation: \"instrumentation.js\" } : {}),\n ...(base.description !== undefined ? { description: base.description } : {}),\n namespaces: base.namespaces,\n commands: base.commands.map((c) => ({ ...c, module: moduleOf.get(c.id)! })),\n }\n\n const generated: Record<string, string> = {\n \"package.json\": `${JSON.stringify({ type: \"module\" }, null, 2)}\\n`,\n \"server.js\": SERVER_JS,\n \"handler.js\": HANDLER_JS,\n }\n const localBin = local ? { bin: local.bin, version: local.version ?? projectVersion(cwd) ?? \"0.0.0\" } : undefined\n if (localBin) {\n generated[LOCAL_BIN_FILE] = localBinJs({ ...localBin, ...(local?.errors !== undefined ? { errors: `${LOCAL_BIN_ERRORS}.js` } : {}) })\n const declaredBin = packageBin(cwd, localBin.bin)\n const expected = `${toPosix(relative(cwd, outDir))}/${LOCAL_BIN_FILE}`\n if (declaredBin === undefined || declaredBin.replace(/^\\.\\//, \"\") !== expected) {\n warnings.push({\n code: \"LOCAL_BIN_UNDECLARED\",\n message: `cli.local: add \"bin\": { \"${localBin.bin}\": \"${expected}\" } to package.json (and include ${toPosix(relative(cwd, outDir))} in \"files\")`,\n })\n }\n }\n for (const [file, content] of Object.entries(generated)) writeFileSync(join(outDir, file), content)\n if (localBin) chmodSync(join(outDir, LOCAL_BIN_FILE), 0o755)\n\n // 고객 CLI (.mext/cli/, §5): manifest · buildId 확정 후, manifest.json 쓰기 전. 로컬 bin 이면 만들지 않는다\n const cli = config.cli && !local ? await buildCustomerCli({ rolldown, cwd, outDir, cli: config.cli, manifest }) : undefined\n\n // manifest.json 은 마지막: 중간 실패한 빌드는 manifest 가 없어 mext start 가 \"mext build 먼저\" 로 거부\n writeFileSync(join(outDir, BUILD_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\\n`)\n\n const files = [\n ...emitted,\n ...Object.keys(generated),\n BUILD_MANIFEST_FILE,\n ...(cli?.files.map((f) => `${CLI_DIR}/${f}`) ?? []),\n ].sort()\n return { outDir, manifest, files, warnings, ...(cli ? { cli } : {}), ...(localBin ? { localBin } : {}) }\n}\n\n/**\n * .mext/bin.js (로컬 bin, cli.local). 계약: mainLocalBin(.mext 폴더 URL, { bin, version, errors? }).\n * 도입 프로젝트 package.json bin 이 이 파일을 가리킨다 (npm 이 symlink 해도 import.meta.url = 실제 경로)\n */\nexport function localBinJs(options: { bin: string; version: string; errors?: string }): string {\n return `#!/usr/bin/env node\n// Generated by mext build. Do not edit.\n// Local bin (cli.local): ${options.bin} help · ${options.bin} <namespace> <command> · ${options.bin} mcp serve\nimport { mainLocalBin } from \"${START_ENTRY}\"\n\nawait mainLocalBin(new URL(\".\", import.meta.url), ${JSON.stringify(options)})\n`\n}\n\n/** package.json bin[name] (문자열 bin = 패키지 이름이 name 일 때) */\nfunction packageBin(cwd: string, name: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as { name?: unknown; bin?: unknown }\n if (typeof pkg.bin === \"string\") return pkg.name === name ? pkg.bin : undefined\n if (typeof pkg.bin === \"object\" && pkg.bin !== null) {\n const v = (pkg.bin as Record<string, unknown>)[name]\n return typeof v === \"string\" ? v : undefined\n }\n } catch {\n // package.json 없음 · 형식 오류 = 미선언\n }\n return undefined\n}\n\n/**\n * .mext/server.js. 계약: startFromBuild(.mext 폴더 URL, { argv, env }) → exit code.\n * argv = server.js 뒤 인자 (--stdio), env = PORT · HOST\n */\nexport const SERVER_JS = `// Generated by mext build. Do not edit.\n// node .mext/server.js [--stdio] (= mext start). Environment: PORT · HOST\nimport { startFromBuild } from \"${START_ENTRY}\"\n\nprocess.exitCode = await startFromBuild(new URL(\".\", import.meta.url), { argv: process.argv.slice(2), env: process.env })\n`\n\n/** .mext/handler.js. 계약: createFetchHandler(.mext 폴더 URL) → { fetch(request: Request): Promise<Response> } (동기 반환) */\nexport const HANDLER_JS = `// Generated by mext build. Do not edit.\n// Web fetch handler (Request → Response) for other runtimes and platforms\nimport { createFetchHandler } from \"${START_ENTRY}\"\n\nexport default createFetchHandler(new URL(\".\", import.meta.url))\n`\n\nasync function importRolldown(): Promise<RolldownModule> {\n // 문자열 변수: 타입 검사 · 번들러가 선택 peer 를 정적 의존으로 취급하지 않게\n const specifier = \"rolldown\"\n return (await import(specifier)) as RolldownModule\n}\n\nfunction isModuleNotFound(error: unknown, pkg: string): boolean {\n const e = error as { code?: unknown; message?: unknown } | null\n if (typeof e !== \"object\" || e === null) return false\n if (e.code !== \"ERR_MODULE_NOT_FOUND\" && e.code !== \"MODULE_NOT_FOUND\") return false\n return typeof e.message !== \"string\" || e.message.includes(`'${pkg}'`) || e.message.includes(`\"${pkg}\"`)\n}\n\n/** 도입 프로젝트의 lock 파일로 패키지 관리자 추정 */\nfunction installCommand(cwd: string): string {\n for (let dir = cwd; ; dir = resolve(dir, \"..\")) {\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) return \"pnpm add -D rolldown\"\n if (existsSync(join(dir, \"yarn.lock\"))) return \"yarn add -D rolldown\"\n if (existsSync(join(dir, \"bun.lock\")) || existsSync(join(dir, \"bun.lockb\"))) return \"bun add -d rolldown\"\n if (existsSync(join(dir, \"package-lock.json\")) || resolve(dir, \"..\") === dir) return \"npm install --save-dev rolldown\"\n }\n}\n\nfunction cleanOutDir(outDir: string): void {\n mkdirSync(outDir, { recursive: true })\n for (const name of readdirSync(outDir)) {\n if (!PRESERVED_ENTRIES.has(name)) rmSync(join(outDir, name), { recursive: true, force: true })\n }\n}\n\n/** \"@scope/pkg/sub\" → \"@scope/pkg\" · \"pkg/sub\" → \"pkg\" */\nfunction packageName(specifier: string): string {\n const parts = specifier.split(\"/\")\n return specifier.startsWith(\"@\") ? parts.slice(0, 2).join(\"/\") : parts[0]!\n}\n\nfunction declaredDependencies(cwd: string): Set<string> {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as Record<string, unknown>\n } catch {\n return new Set()\n }\n const names = new Set<string>()\n if (typeof pkg.name === \"string\") names.add(pkg.name)\n for (const field of [\"dependencies\", \"peerDependencies\", \"optionalDependencies\"]) {\n const deps = pkg[field]\n if (typeof deps === \"object\" && deps !== null) for (const name of Object.keys(deps)) names.add(name)\n }\n return names\n}\n\n/** D3: mextVersion · manifestHash · 번들 chunk (파일 이름 · 코드) 의 sha256 앞 16자. 같은 입력 = 같은 buildId */\nfunction buildIdOf(version: string, hash: string, output: readonly RolldownOutputItem[]): string {\n const h = createHash(\"sha256\").update(`${version}\\n${hash}\\n`)\n for (const item of [...output].sort((a, b) => (a.fileName < b.fileName ? -1 : 1))) {\n if (item.type !== \"chunk\") continue\n h.update(`${item.fileName}\\0${item.code ?? \"\"}\\0`)\n }\n return h.digest(\"hex\").slice(0, 16)\n}\n\nfunction toPosix(path: string): string {\n return path.split(sep).join(\"/\")\n}\n"]}
package/dist/cli/build.js CHANGED
@@ -49,6 +49,9 @@ export function formatBuildSummary(result, cwd) {
49
49
  const kb = (cli.size / 1024).toFixed(1);
50
50
  lines.push(`Customer CLI: ${dir}/cli/ (${cli.packageName}@${cli.version}, bin ${cli.bin}, ${cli.form}, ${kb} KB). Publish: npm publish ${dir}/cli`);
51
51
  }
52
+ const local = result.localBin;
53
+ if (local)
54
+ lines.push(`Local bin: ${dir}/bin.js (bin ${local.bin}@${local.version}). Run: ${local.bin} help (after npm link · npm install -g)`);
52
55
  return `${lines.join("\n")}\n`;
53
56
  }
54
57
  /** readOnly · destructive · confirm. 아무것도 없으면 write */
@@ -77,6 +80,8 @@ export function buildHelp() {
77
80
  ["commands/**/*.js", "one module per command"],
78
81
  ["chunks/", "shared code"],
79
82
  ["cli/", "customer CLI npm package (when mext.config.ts has cli): package.json · bin.js · README.md"],
83
+ ["bin.js", "local bin (cli.local: true, instead of cli/). package.json bin points here"],
84
+ ["cli-errors.js", "cli.errors bundle for the local bin"],
80
85
  ]).map((l) => ` ${l}`),
81
86
  "",
82
87
  "Requires rolldown (npm install --save-dev rolldown). Package imports stay external.",
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"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","sourcesContent":["import { relative } from \"node:path\"\nimport { buildProject, type BuildOptions, type BuildResult } from \"../build.ts\"\nimport type { BuildManifestCommand } from \"../manifest.ts\"\nimport { table } from \"./help.ts\"\nimport type { CliIo } from \"./io.ts\"\nimport { EXIT, exitCodeOf, formatError } from \"./output.ts\"\n\nexport const BUILD_PROG = \"mext build\"\n\n/**\n * mext build: commands/ → .mext/. 경고 · 오류 = stderr, 성공 요약 · 명령어 표 = stdout.\n * 검증 오류는 MextBuildError 하나에 모아 한 번에 출력, exit 1\n */\nexport async function runBuild(argv: readonly string[], io: CliIo, options: Omit<BuildOptions, \"cwd\"> = {}): Promise<number> {\n for (const tok of argv) {\n if (tok === \"--help\" || tok === \"-h\") {\n io.stdout.write(buildHelp())\n return EXIT.ok\n }\n io.stderr.write(`error: unexpected argument \"${tok}\"\\nRun \"${BUILD_PROG} --help\" for usage.\\n`)\n return EXIT.usage\n }\n let result: BuildResult\n try {\n result = await buildProject({ ...options, cwd: io.cwd })\n } catch (error) {\n io.stderr.write(formatError(error, BUILD_PROG))\n return exitCodeOf(error)\n }\n for (const w of result.warnings) io.stderr.write(`warning: ${w.message}\\n`)\n io.stdout.write(formatBuildSummary(result, io.cwd))\n return EXIT.ok\n}\n\n/** 성공 요약 + 명령어 표 (ID · 안전성 · scopes), next build 의 route 표와 같은 역할 */\nexport function formatBuildSummary(result: BuildResult, cwd: string): string {\n const { manifest } = result\n const dir = relative(cwd, result.outDir) || \".\"\n const n = manifest.commands.length\n const lines = [\n `Built ${dir}/ (${n} command${n === 1 ? \"\" : \"s\"}, buildId ${manifest.buildId})`,\n \"\",\n ...table([\n [\"Command\", \"Safety\", \"Scopes\"],\n ...manifest.commands.map((c) => [c.id, safetyOf(c), c.scopes.join(\", \") || \"-\"]),\n ]).map((l) => ` ${l}`),\n \"\",\n `Start: node ${dir}/server.js [--stdio]`,\n ]\n const cli = result.cli\n if (cli) {\n const kb = (cli.size / 1024).toFixed(1)\n lines.push(\n `Customer CLI: ${dir}/cli/ (${cli.packageName}@${cli.version}, bin ${cli.bin}, ${cli.form}, ${kb} KB). Publish: npm publish ${dir}/cli`,\n )\n }\n return `${lines.join(\"\\n\")}\\n`\n}\n\n/** readOnly · destructive · confirm. 아무것도 없으면 write */\nfunction safetyOf(c: BuildManifestCommand): string {\n const flags = [\n ...(c.readOnly ? [\"readOnly\"] : []),\n ...(c.destructive ? [\"destructive\"] : []),\n ...(c.requiresConfirmation ? [\"confirm\"] : []),\n ]\n return flags.length === 0 ? \"write\" : flags.join(\" · \")\n}\n\nexport function buildHelp(): string {\n const lines = [\n \"Build commands/ into .mext/ for production (mext start · node .mext/server.js).\",\n \"\",\n \"Usage:\",\n ` ${BUILD_PROG}`,\n \"\",\n \"Output (.mext/):\",\n ...table([\n [\"manifest.json\", \"schemaVersion · mextVersion · buildId · manifestHash · namespaces · commands (+ module)\"],\n [\"server.js\", \"node .mext/server.js [--stdio]. PORT · HOST environment variables\"],\n [\"handler.js\", \"export default { fetch(request) } (Web Request → Response)\"],\n [\"config.js\", \"mext.config.ts bundle (when present)\"],\n [\"instrumentation.js\", \"instrumentation.ts bundle (when present)\"],\n [\"commands/**/*.js\", \"one module per command\"],\n [\"chunks/\", \"shared code\"],\n [\"cli/\", \"customer CLI npm package (when mext.config.ts has cli): package.json · bin.js · README.md\"],\n ]).map((l) => ` ${l}`),\n \"\",\n \"Requires rolldown (npm install --save-dev rolldown). Package imports stay external.\",\n \"All validation errors are reported at once (exit 1).\",\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n"]}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"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","sourcesContent":["import { relative } from \"node:path\"\nimport { buildProject, type BuildOptions, type BuildResult } from \"../build.ts\"\nimport type { BuildManifestCommand } from \"../manifest.ts\"\nimport { table } from \"./help.ts\"\nimport type { CliIo } from \"./io.ts\"\nimport { EXIT, exitCodeOf, formatError } from \"./output.ts\"\n\nexport const BUILD_PROG = \"mext build\"\n\n/**\n * mext build: commands/ → .mext/. 경고 · 오류 = stderr, 성공 요약 · 명령어 표 = stdout.\n * 검증 오류는 MextBuildError 하나에 모아 한 번에 출력, exit 1\n */\nexport async function runBuild(argv: readonly string[], io: CliIo, options: Omit<BuildOptions, \"cwd\"> = {}): Promise<number> {\n for (const tok of argv) {\n if (tok === \"--help\" || tok === \"-h\") {\n io.stdout.write(buildHelp())\n return EXIT.ok\n }\n io.stderr.write(`error: unexpected argument \"${tok}\"\\nRun \"${BUILD_PROG} --help\" for usage.\\n`)\n return EXIT.usage\n }\n let result: BuildResult\n try {\n result = await buildProject({ ...options, cwd: io.cwd })\n } catch (error) {\n io.stderr.write(formatError(error, BUILD_PROG))\n return exitCodeOf(error)\n }\n for (const w of result.warnings) io.stderr.write(`warning: ${w.message}\\n`)\n io.stdout.write(formatBuildSummary(result, io.cwd))\n return EXIT.ok\n}\n\n/** 성공 요약 + 명령어 표 (ID · 안전성 · scopes), next build 의 route 표와 같은 역할 */\nexport function formatBuildSummary(result: BuildResult, cwd: string): string {\n const { manifest } = result\n const dir = relative(cwd, result.outDir) || \".\"\n const n = manifest.commands.length\n const lines = [\n `Built ${dir}/ (${n} command${n === 1 ? \"\" : \"s\"}, buildId ${manifest.buildId})`,\n \"\",\n ...table([\n [\"Command\", \"Safety\", \"Scopes\"],\n ...manifest.commands.map((c) => [c.id, safetyOf(c), c.scopes.join(\", \") || \"-\"]),\n ]).map((l) => ` ${l}`),\n \"\",\n `Start: node ${dir}/server.js [--stdio]`,\n ]\n const cli = result.cli\n if (cli) {\n const kb = (cli.size / 1024).toFixed(1)\n lines.push(\n `Customer CLI: ${dir}/cli/ (${cli.packageName}@${cli.version}, bin ${cli.bin}, ${cli.form}, ${kb} KB). Publish: npm publish ${dir}/cli`,\n )\n }\n const local = result.localBin\n if (local) lines.push(`Local bin: ${dir}/bin.js (bin ${local.bin}@${local.version}). Run: ${local.bin} help (after npm link · npm install -g)`)\n return `${lines.join(\"\\n\")}\\n`\n}\n\n/** readOnly · destructive · confirm. 아무것도 없으면 write */\nfunction safetyOf(c: BuildManifestCommand): string {\n const flags = [\n ...(c.readOnly ? [\"readOnly\"] : []),\n ...(c.destructive ? [\"destructive\"] : []),\n ...(c.requiresConfirmation ? [\"confirm\"] : []),\n ]\n return flags.length === 0 ? \"write\" : flags.join(\" · \")\n}\n\nexport function buildHelp(): string {\n const lines = [\n \"Build commands/ into .mext/ for production (mext start · node .mext/server.js).\",\n \"\",\n \"Usage:\",\n ` ${BUILD_PROG}`,\n \"\",\n \"Output (.mext/):\",\n ...table([\n [\"manifest.json\", \"schemaVersion · mextVersion · buildId · manifestHash · namespaces · commands (+ module)\"],\n [\"server.js\", \"node .mext/server.js [--stdio]. PORT · HOST environment variables\"],\n [\"handler.js\", \"export default { fetch(request) } (Web Request → Response)\"],\n [\"config.js\", \"mext.config.ts bundle (when present)\"],\n [\"instrumentation.js\", \"instrumentation.ts bundle (when present)\"],\n [\"commands/**/*.js\", \"one module per command\"],\n [\"chunks/\", \"shared code\"],\n [\"cli/\", \"customer CLI npm package (when mext.config.ts has cli): package.json · bin.js · README.md\"],\n [\"bin.js\", \"local bin (cli.local: true, instead of cli/). package.json bin points here\"],\n [\"cli-errors.js\", \"cli.errors bundle for the local bin\"],\n ]).map((l) => ` ${l}`),\n \"\",\n \"Requires rolldown (npm install --save-dev rolldown). Package imports stay external.\",\n \"All validation errors are reported at once (exit 1).\",\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n"]}
@@ -17,7 +17,7 @@ export declare class CliInterrupted extends Error {
17
17
  */
18
18
  export declare function exitCodeOf(error: unknown): number;
19
19
  /**
20
- * --json 오류 본문. MextError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
20
+ * --json 오류 본문. MextError · CommandError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
21
21
  * SIGINT = INTERRUPTED, 핸들러 예외 = EXECUTION_ERROR
22
22
  */
23
23
  export declare function errorJson(error: unknown): Record<string, unknown>;
@@ -1,4 +1,4 @@
1
- import { isMextError, MextBuildError, MextError } from "../errors.js";
1
+ import { CommandError, isMextError, MextBuildError, MextError } from "../errors.js";
2
2
  import { toKebab } from "./args.js";
3
3
  import { cliPath, table } from "./help.js";
4
4
  /** exit code (spec §5) */
@@ -37,11 +37,11 @@ export function exitCodeOf(error) {
37
37
  return EXIT.error;
38
38
  }
39
39
  /**
40
- * --json 오류 본문. MextError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
40
+ * --json 오류 본문. MextError · CommandError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
41
41
  * SIGINT = INTERRUPTED, 핸들러 예외 = EXECUTION_ERROR
42
42
  */
43
43
  export function errorJson(error) {
44
- if (error instanceof MextError)
44
+ if (error instanceof MextError || error instanceof CommandError)
45
45
  return error.toJSON();
46
46
  if (error instanceof MextBuildError)
47
47
  return { code: "BUILD_ERROR", message: error.message, issues: error.issues };