@obov/mext 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.js +67 -4
- package/dist/build.js.map +1 -1
- package/dist/cli/ask-url.d.ts +16 -0
- package/dist/cli/ask-url.js +27 -0
- package/dist/cli/ask-url.js.map +1 -0
- package/dist/cli/build.js +7 -1
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev-server.d.ts +7 -1
- package/dist/cli/dev-server.js +15 -2
- package/dist/cli/dev-server.js.map +1 -1
- package/dist/cli/dev.js +27 -7
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/login.d.ts +5 -0
- package/dist/cli/login.js +11 -7
- package/dist/cli/login.js.map +1 -1
- package/dist/cli/logout.js +4 -6
- package/dist/cli/logout.js.map +1 -1
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +1 -2
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/run.js +21 -5
- package/dist/cli/run.js.map +1 -1
- package/dist/client/cache.d.ts +14 -4
- package/dist/client/cache.js +10 -3
- package/dist/client/cache.js.map +1 -1
- package/dist/client/main.js +11 -10
- package/dist/client/main.js.map +1 -1
- package/dist/client/manifest.d.ts +13 -1
- package/dist/client/manifest.js +43 -0
- package/dist/client/manifest.js.map +1 -1
- package/dist/client/runner.d.ts +3 -1
- package/dist/client/runner.js +80 -19
- package/dist/client/runner.js.map +1 -1
- package/dist/client/task.d.ts +47 -0
- package/dist/client/task.js +126 -0
- package/dist/client/task.js.map +1 -0
- package/dist/command.d.ts +30 -3
- package/dist/command.js +16 -0
- package/dist/command.js.map +1 -1
- package/dist/config.d.ts +40 -6
- package/dist/config.js +57 -8
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +17 -5
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/instrumentation.d.ts +14 -2
- package/dist/instrumentation.js +12 -4
- package/dist/instrumentation.js.map +1 -1
- package/dist/invoke.d.ts +38 -0
- package/dist/invoke.js +72 -3
- package/dist/invoke.js.map +1 -1
- package/dist/local-bin.js +17 -9
- package/dist/local-bin.js.map +1 -1
- package/dist/manifest.d.ts +48 -3
- package/dist/manifest.js +14 -3
- package/dist/manifest.js.map +1 -1
- package/dist/mcp/constants.d.ts +4 -0
- package/dist/mcp/constants.js +4 -0
- package/dist/mcp/constants.js.map +1 -1
- package/dist/mcp/dispatch.d.ts +21 -7
- package/dist/mcp/dispatch.js +42 -7
- package/dist/mcp/dispatch.js.map +1 -1
- package/dist/mcp/http.d.ts +23 -5
- package/dist/mcp/http.js +64 -9
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/index.d.ts +14 -6
- package/dist/mcp/index.js +7 -3
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mrtr.d.ts +40 -8
- package/dist/mcp/mrtr.js +32 -11
- package/dist/mcp/mrtr.js.map +1 -1
- package/dist/mcp/prompts.d.ts +25 -0
- package/dist/mcp/prompts.js +164 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +48 -0
- package/dist/mcp/resources.js +142 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +30 -5
- package/dist/mcp/server.js +143 -16
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts +5 -0
- package/dist/mcp/stdio.js +16 -3
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp/subscriptions.d.ts +61 -0
- package/dist/mcp/subscriptions.js +188 -0
- package/dist/mcp/subscriptions.js.map +1 -0
- package/dist/mcp/tasks.d.ts +127 -0
- package/dist/mcp/tasks.js +448 -0
- package/dist/mcp/tasks.js.map +1 -0
- package/dist/module-files.d.ts +24 -0
- package/dist/module-files.js +97 -0
- package/dist/module-files.js.map +1 -0
- package/dist/prompt.d.ts +133 -0
- package/dist/prompt.js +222 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js.map +1 -1
- package/dist/remote/client.d.ts +17 -1
- package/dist/remote/client.js +18 -2
- package/dist/remote/client.js.map +1 -1
- package/dist/resource.d.ts +169 -0
- package/dist/resource.js +301 -0
- package/dist/resource.js.map +1 -0
- package/dist/route.d.ts +71 -0
- package/dist/route.js +280 -0
- package/dist/route.js.map +1 -0
- package/dist/start.d.ts +14 -3
- package/dist/start.js +76 -11
- package/dist/start.js.map +1 -1
- package/dist/state.d.ts +38 -0
- package/dist/state.js +98 -0
- package/dist/state.js.map +1 -0
- package/package.json +1 -1
package/dist/instrumentation.js
CHANGED
|
@@ -99,17 +99,24 @@ const TRACER_NAME = "@obov/mext";
|
|
|
99
99
|
* 취소 (signal abort · MRTR 대기) = status 없음.
|
|
100
100
|
*/
|
|
101
101
|
export function observeCommand(command, options, signal, fn) {
|
|
102
|
+
return observeSpan(`mext.command ${command}`, { "mext.command.id": command }, options, signal, fn);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* observeCommand 의 일반형 (자원 = `mext.resource <uri 템플릿>`, §12.3).
|
|
106
|
+
* attributes 에 mext.transport · mext.principal.kind 를 더한다. 실패 · 취소 처리는 observeCommand 와 같다
|
|
107
|
+
*/
|
|
108
|
+
export function observeSpan(name, attributes, options, signal, fn) {
|
|
102
109
|
const otel = options.instrumentation?.otel;
|
|
103
110
|
if (!otel)
|
|
104
111
|
return fn();
|
|
105
112
|
const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active();
|
|
106
|
-
const
|
|
107
|
-
|
|
113
|
+
const all = {
|
|
114
|
+
...attributes,
|
|
108
115
|
"mext.transport": options.transport ?? "local",
|
|
109
116
|
"mext.principal.kind": options.principal.kind,
|
|
110
117
|
};
|
|
111
118
|
const tracer = otel.trace.getTracer(TRACER_NAME);
|
|
112
|
-
return tracer.startActiveSpan(
|
|
119
|
+
return tracer.startActiveSpan(name, { attributes: all }, parent, async (span) => {
|
|
113
120
|
try {
|
|
114
121
|
return await fn();
|
|
115
122
|
}
|
|
@@ -136,12 +143,13 @@ export function observeCommand(command, options, signal, fn) {
|
|
|
136
143
|
* 핸들러 예외 → onCommandError (await). MextError · CommandError · 취소 · MRTR 대기는 보고하지 않는다.
|
|
137
144
|
* onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.
|
|
138
145
|
*/
|
|
139
|
-
export async function reportCommandError(error, command, options, signal) {
|
|
146
|
+
export async function reportCommandError(error, command, options, signal, extra = {}) {
|
|
140
147
|
const hook = options.instrumentation?.onCommandError;
|
|
141
148
|
if (!hook || isDeclaredError(error) || signal?.aborted)
|
|
142
149
|
return;
|
|
143
150
|
const info = {
|
|
144
151
|
command,
|
|
152
|
+
...extra,
|
|
145
153
|
transport: options.transport ?? "local",
|
|
146
154
|
principal: { id: options.principal.id, kind: options.principal.kind },
|
|
147
155
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { isAbsolute, join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { isDeclaredError } from \"./errors.ts\"\nimport type { Principal } from \"./principal.ts\"\n\n/**\n * instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).\n * register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.\n * span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).\n */\n\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n\n/** 핸들러를 실행한 어댑터. 원격 CLI 는 핸들러를 실행하지 않으므로 없다 */\nexport type Transport = \"http\" | \"stdio\" | \"local\"\n\nexport interface CommandErrorInfo {\n /** 정식 명령어 ID */\n command: string\n transport: Transport\n /** scopes · claims 는 넘기지 않는다 */\n principal: { id: string; kind: Principal[\"kind\"] }\n}\n\n/** instrumentation.ts 의 export */\nexport interface InstrumentationModule {\n register?: () => unknown\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n}\n\n/** 어댑터가 InvokeOptions.instrumentation 으로 넘기는 로드 결과 */\nexport interface Instrumentation {\n /** 핸들러 예외 (MextError 외, 취소 · MRTR 대기 제외). 자체 예외는 stderr 로 보고하고 응답은 바꾸지 않는다 */\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n /** @opentelemetry/api. undefined = span 없음 */\n otel?: OtelApi\n /** onCommandError 의 예외 보고. 기본 stderr */\n onerror?: (error: unknown) => void\n}\n\nexport interface LoadInstrumentationOptions {\n /** @opentelemetry/api 로더 (테스트 주입). 기본 = 동적 import, 결과는 프로세스당 캐시 */\n importOtel?: () => Promise<unknown>\n onerror?: (error: unknown) => void\n}\n\n/** mext 가 쓰는 @opentelemetry/api 의 부분 (타입 의존 없이) */\nexport interface OtelApi {\n trace: { getTracer(name: string, version?: string): OtelTracer }\n context: { active(): unknown }\n propagation: { extract(context: unknown, carrier: unknown): unknown }\n SpanStatusCode: { ERROR: number }\n}\n\ninterface OtelTracer {\n startActiveSpan<T>(name: string, options: { attributes: Record<string, string> }, context: unknown, fn: (span: OtelSpan) => T): T\n}\n\ninterface OtelSpan {\n setAttribute(key: string, value: string): unknown\n setStatus(status: { code: number; message?: string }): unknown\n recordException(exception: unknown): unknown\n end(): void\n}\n\n/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */\nexport function findInstrumentation(cwd: string = process.cwd()): string | undefined {\n const file = join(resolve(cwd), INSTRUMENTATION_FILE)\n return existsSync(file) ? file : undefined\n}\n\n/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */\nexport function loadProjectInstrumentation(\n cwd: string = process.cwd(),\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n return loadInstrumentation(findInstrumentation(cwd), options)\n}\n\nconst loaded = new Map<string, Promise<InstrumentationModule>>()\nconst loadedModules = new WeakMap<object, Promise<InstrumentationModule>>()\n\n/**\n * instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.\n * 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).\n * source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.\n */\nexport async function loadInstrumentation(\n source?: string | URL | InstrumentationModule,\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n const [mod, otel] = await Promise.all([\n source === undefined ? Promise.resolve<InstrumentationModule>({}) : registerOnce(source),\n options.importOtel ? resolveOtel(options.importOtel) : defaultOtel(),\n ])\n return {\n ...(mod.onCommandError ? { onCommandError: mod.onCommandError } : {}),\n ...(otel ? { otel } : {}),\n ...(options.onerror ? { onerror: options.onerror } : {}),\n }\n}\n\nfunction registerOnce(source: string | URL | InstrumentationModule): Promise<InstrumentationModule> {\n if (typeof source === \"string\" || source instanceof URL) {\n const isPath = typeof source === \"string\" && (isAbsolute(source) || !/^[a-z][a-z0-9+.-]+:/i.test(source))\n const href = isPath ? pathToFileURL(resolve(source as string)).href : String(source)\n return memo(loaded, href, () => import(href).then((m: unknown) => register(m, href)))\n }\n return memo(loadedModules, source, () => register(source, \"instrumentation\"))\n}\n\nfunction memo<K>(\n cache: { get(k: K): Promise<InstrumentationModule> | undefined; set(k: K, v: Promise<InstrumentationModule>): unknown; delete(k: K): unknown },\n key: K,\n load: () => Promise<InstrumentationModule>,\n): Promise<InstrumentationModule> {\n let pending = cache.get(key)\n if (!pending) {\n pending = load().catch((error: unknown) => {\n cache.delete(key)\n throw error\n })\n cache.set(key, pending)\n }\n return pending\n}\n\nasync function register(value: unknown, label: string): Promise<InstrumentationModule> {\n const mod = (value ?? {}) as Record<string, unknown>\n for (const key of [\"register\", \"onCommandError\"]) {\n if (mod[key] !== undefined && typeof mod[key] !== \"function\") {\n throw new TypeError(`${label}: export ${key} must be a function`)\n }\n }\n const hooks = mod as InstrumentationModule\n await hooks.register?.()\n return hooks\n}\n\n/* @opentelemetry/api (선택 peer). 번들러가 해석 · 포함하지 않도록 specifier 는 변수 (R5) */\n\nconst OTEL_API = \"@opentelemetry/api\"\nlet otelDefault: Promise<OtelApi | undefined> | undefined\n\nfunction defaultOtel(): Promise<OtelApi | undefined> {\n otelDefault ??= resolveOtel(() => import(OTEL_API))\n return otelDefault\n}\n\nasync function resolveOtel(importer: () => Promise<unknown>): Promise<OtelApi | undefined> {\n try {\n const mod = (await importer()) as Partial<OtelApi> & { default?: Partial<OtelApi> }\n const api = mod.trace ? mod : mod.default\n return api?.trace && api.context && api.propagation && api.SpanStatusCode ? (api as OtelApi) : undefined\n } catch {\n return undefined\n }\n}\n\n/* invoke 에서 사용 */\n\nconst TRACER_NAME = \"@obov/mext\"\n\nexport interface ObserveOptions {\n instrumentation?: Instrumentation\n transport?: Transport\n principal: Principal\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate) */\n traceContext?: Record<string, unknown>\n}\n\n/**\n * 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).\n * 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.\n * 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError · CommandError 메시지는 입력 일부를 담을 수 있어 제외).\n * 취소 (signal abort · MRTR 대기) = status 없음.\n */\nexport function observeCommand<T>(\n command: string,\n options: ObserveOptions,\n signal: () => AbortSignal | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n const otel = options.instrumentation?.otel\n if (!otel) return fn()\n const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active()\n const attributes = {\n \"mext.command.id\": command,\n \"mext.transport\": options.transport ?? \"local\",\n \"mext.principal.kind\": options.principal.kind,\n }\n const tracer = otel.trace.getTracer(TRACER_NAME)\n return tracer.startActiveSpan(`mext.command ${command}`, { attributes }, parent, async (span) => {\n try {\n return await fn()\n } catch (error) {\n if (!signal()?.aborted) {\n if (isDeclaredError(error)) {\n span.setAttribute(\"mext.error.code\", error.code)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error.code })\n } else {\n span.setAttribute(\"mext.error.code\", \"EXECUTION_ERROR\")\n span.recordException(error)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) })\n }\n }\n throw error\n } finally {\n span.end()\n }\n })\n}\n\n/**\n * 핸들러 예외 → onCommandError (await). MextError · CommandError · 취소 · MRTR 대기는 보고하지 않는다.\n * onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.\n */\nexport async function reportCommandError(\n error: unknown,\n command: string,\n options: ObserveOptions,\n signal: AbortSignal | undefined,\n): Promise<void> {\n const hook = options.instrumentation?.onCommandError\n if (!hook || isDeclaredError(error) || signal?.aborted) return\n const info: CommandErrorInfo = {\n command,\n transport: options.transport ?? \"local\",\n principal: { id: options.principal.id, kind: options.principal.kind },\n }\n try {\n await hook(error, info)\n } catch (hookError) {\n const onerror = options.instrumentation?.onerror ?? defaultOnError\n try {\n onerror(hookError)\n } catch {\n // 보고는 응답을 바꾸지 않는다\n }\n }\n}\n\nfunction defaultOnError(error: unknown): void {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error)\n process.stderr.write(`mext: onCommandError threw: ${message}\\n`)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { isAbsolute, join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { isDeclaredError } from \"./errors.ts\"\nimport type { Principal } from \"./principal.ts\"\n\n/**\n * instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).\n * register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.\n * span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).\n */\n\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n\n/** 핸들러를 실행한 어댑터. 원격 CLI 는 핸들러를 실행하지 않으므로 없다 */\nexport type Transport = \"http\" | \"stdio\" | \"local\"\n\nexport interface CommandErrorInfo {\n /** 정식 명령어 ID. 자원 핸들러 (read · list · complete) 오류 = 자원 uri (템플릿 원문), 프롬프트 핸들러 오류 = 프롬프트 이름 */\n command: string\n /** 자원 핸들러 오류일 때 자원 uri (템플릿 원문, §12.3) */\n resource?: string\n /** 프롬프트 핸들러 (render · complete) 오류일 때 프롬프트 이름 (§12.3) */\n prompt?: string\n transport: Transport\n /** scopes · claims 는 넘기지 않는다 */\n principal: { id: string; kind: Principal[\"kind\"] }\n}\n\n/** instrumentation.ts 의 export */\nexport interface InstrumentationModule {\n register?: () => unknown\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n}\n\n/** 어댑터가 InvokeOptions.instrumentation 으로 넘기는 로드 결과 */\nexport interface Instrumentation {\n /** 핸들러 예외 (MextError 외, 취소 · MRTR 대기 제외). 자체 예외는 stderr 로 보고하고 응답은 바꾸지 않는다 */\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n /** @opentelemetry/api. undefined = span 없음 */\n otel?: OtelApi\n /** onCommandError 의 예외 보고. 기본 stderr */\n onerror?: (error: unknown) => void\n}\n\nexport interface LoadInstrumentationOptions {\n /** @opentelemetry/api 로더 (테스트 주입). 기본 = 동적 import, 결과는 프로세스당 캐시 */\n importOtel?: () => Promise<unknown>\n onerror?: (error: unknown) => void\n}\n\n/** mext 가 쓰는 @opentelemetry/api 의 부분 (타입 의존 없이) */\nexport interface OtelApi {\n trace: { getTracer(name: string, version?: string): OtelTracer }\n context: { active(): unknown }\n propagation: { extract(context: unknown, carrier: unknown): unknown }\n SpanStatusCode: { ERROR: number }\n}\n\ninterface OtelTracer {\n startActiveSpan<T>(name: string, options: { attributes: Record<string, string> }, context: unknown, fn: (span: OtelSpan) => T): T\n}\n\ninterface OtelSpan {\n setAttribute(key: string, value: string): unknown\n setStatus(status: { code: number; message?: string }): unknown\n recordException(exception: unknown): unknown\n end(): void\n}\n\n/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */\nexport function findInstrumentation(cwd: string = process.cwd()): string | undefined {\n const file = join(resolve(cwd), INSTRUMENTATION_FILE)\n return existsSync(file) ? file : undefined\n}\n\n/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */\nexport function loadProjectInstrumentation(\n cwd: string = process.cwd(),\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n return loadInstrumentation(findInstrumentation(cwd), options)\n}\n\nconst loaded = new Map<string, Promise<InstrumentationModule>>()\nconst loadedModules = new WeakMap<object, Promise<InstrumentationModule>>()\n\n/**\n * instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.\n * 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).\n * source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.\n */\nexport async function loadInstrumentation(\n source?: string | URL | InstrumentationModule,\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n const [mod, otel] = await Promise.all([\n source === undefined ? Promise.resolve<InstrumentationModule>({}) : registerOnce(source),\n options.importOtel ? resolveOtel(options.importOtel) : defaultOtel(),\n ])\n return {\n ...(mod.onCommandError ? { onCommandError: mod.onCommandError } : {}),\n ...(otel ? { otel } : {}),\n ...(options.onerror ? { onerror: options.onerror } : {}),\n }\n}\n\nfunction registerOnce(source: string | URL | InstrumentationModule): Promise<InstrumentationModule> {\n if (typeof source === \"string\" || source instanceof URL) {\n const isPath = typeof source === \"string\" && (isAbsolute(source) || !/^[a-z][a-z0-9+.-]+:/i.test(source))\n const href = isPath ? pathToFileURL(resolve(source as string)).href : String(source)\n return memo(loaded, href, () => import(href).then((m: unknown) => register(m, href)))\n }\n return memo(loadedModules, source, () => register(source, \"instrumentation\"))\n}\n\nfunction memo<K>(\n cache: { get(k: K): Promise<InstrumentationModule> | undefined; set(k: K, v: Promise<InstrumentationModule>): unknown; delete(k: K): unknown },\n key: K,\n load: () => Promise<InstrumentationModule>,\n): Promise<InstrumentationModule> {\n let pending = cache.get(key)\n if (!pending) {\n pending = load().catch((error: unknown) => {\n cache.delete(key)\n throw error\n })\n cache.set(key, pending)\n }\n return pending\n}\n\nasync function register(value: unknown, label: string): Promise<InstrumentationModule> {\n const mod = (value ?? {}) as Record<string, unknown>\n for (const key of [\"register\", \"onCommandError\"]) {\n if (mod[key] !== undefined && typeof mod[key] !== \"function\") {\n throw new TypeError(`${label}: export ${key} must be a function`)\n }\n }\n const hooks = mod as InstrumentationModule\n await hooks.register?.()\n return hooks\n}\n\n/* @opentelemetry/api (선택 peer). 번들러가 해석 · 포함하지 않도록 specifier 는 변수 (R5) */\n\nconst OTEL_API = \"@opentelemetry/api\"\nlet otelDefault: Promise<OtelApi | undefined> | undefined\n\nfunction defaultOtel(): Promise<OtelApi | undefined> {\n otelDefault ??= resolveOtel(() => import(OTEL_API))\n return otelDefault\n}\n\nasync function resolveOtel(importer: () => Promise<unknown>): Promise<OtelApi | undefined> {\n try {\n const mod = (await importer()) as Partial<OtelApi> & { default?: Partial<OtelApi> }\n const api = mod.trace ? mod : mod.default\n return api?.trace && api.context && api.propagation && api.SpanStatusCode ? (api as OtelApi) : undefined\n } catch {\n return undefined\n }\n}\n\n/* invoke 에서 사용 */\n\nconst TRACER_NAME = \"@obov/mext\"\n\nexport interface ObserveOptions {\n instrumentation?: Instrumentation\n transport?: Transport\n principal: Principal\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate) */\n traceContext?: Record<string, unknown>\n}\n\n/**\n * 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).\n * 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.\n * 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError · CommandError 메시지는 입력 일부를 담을 수 있어 제외).\n * 취소 (signal abort · MRTR 대기) = status 없음.\n */\nexport function observeCommand<T>(\n command: string,\n options: ObserveOptions,\n signal: () => AbortSignal | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n return observeSpan(`mext.command ${command}`, { \"mext.command.id\": command }, options, signal, fn)\n}\n\n/**\n * observeCommand 의 일반형 (자원 = `mext.resource <uri 템플릿>`, §12.3).\n * attributes 에 mext.transport · mext.principal.kind 를 더한다. 실패 · 취소 처리는 observeCommand 와 같다\n */\nexport function observeSpan<T>(\n name: string,\n attributes: Record<string, string>,\n options: ObserveOptions,\n signal: () => AbortSignal | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n const otel = options.instrumentation?.otel\n if (!otel) return fn()\n const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active()\n const all = {\n ...attributes,\n \"mext.transport\": options.transport ?? \"local\",\n \"mext.principal.kind\": options.principal.kind,\n }\n const tracer = otel.trace.getTracer(TRACER_NAME)\n return tracer.startActiveSpan(name, { attributes: all }, parent, async (span) => {\n try {\n return await fn()\n } catch (error) {\n if (!signal()?.aborted) {\n if (isDeclaredError(error)) {\n span.setAttribute(\"mext.error.code\", error.code)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error.code })\n } else {\n span.setAttribute(\"mext.error.code\", \"EXECUTION_ERROR\")\n span.recordException(error)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) })\n }\n }\n throw error\n } finally {\n span.end()\n }\n })\n}\n\n/**\n * 핸들러 예외 → onCommandError (await). MextError · CommandError · 취소 · MRTR 대기는 보고하지 않는다.\n * onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.\n */\nexport async function reportCommandError(\n error: unknown,\n command: string,\n options: ObserveOptions,\n signal: AbortSignal | undefined,\n extra: { resource?: string; prompt?: string } = {},\n): Promise<void> {\n const hook = options.instrumentation?.onCommandError\n if (!hook || isDeclaredError(error) || signal?.aborted) return\n const info: CommandErrorInfo = {\n command,\n ...extra,\n transport: options.transport ?? \"local\",\n principal: { id: options.principal.id, kind: options.principal.kind },\n }\n try {\n await hook(error, info)\n } catch (hookError) {\n const onerror = options.instrumentation?.onerror ?? defaultOnError\n try {\n onerror(hookError)\n } catch {\n // 보고는 응답을 바꾸지 않는다\n }\n }\n}\n\nfunction defaultOnError(error: unknown): void {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error)\n process.stderr.write(`mext: onCommandError threw: ${message}\\n`)\n}\n"]}
|
package/dist/invoke.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import type { CommandContext, InputSchema, MextApp, ProgressUpdate } from "./command.ts";
|
|
3
|
+
import { MextError } from "./errors.ts";
|
|
3
4
|
import { type Instrumentation, type Transport } from "./instrumentation.ts";
|
|
4
5
|
import { findCommand } from "./lookup.ts";
|
|
5
6
|
import { type Principal } from "./principal.ts";
|
|
@@ -16,6 +17,11 @@ export type AskHandlerRequest = {
|
|
|
16
17
|
message: string;
|
|
17
18
|
schema: InputSchema;
|
|
18
19
|
requestedSchema: JsonSchema;
|
|
20
|
+
} | {
|
|
21
|
+
command: string;
|
|
22
|
+
kind: "url";
|
|
23
|
+
message: string;
|
|
24
|
+
url: string;
|
|
19
25
|
};
|
|
20
26
|
export interface InvokeOptions {
|
|
21
27
|
principal: Principal;
|
|
@@ -34,11 +40,35 @@ export interface InvokeOptions {
|
|
|
34
40
|
transport?: Transport;
|
|
35
41
|
/** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate). span 의 부모 */
|
|
36
42
|
traceContext?: Record<string, unknown>;
|
|
43
|
+
/** ctx.resourceUpdated (§12.5). 기본 = no-op */
|
|
44
|
+
resourceUpdated?: (uri: string) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* tasks 확장 (§12.6). 입력 검증 · 권한 · requiresConfirmation 통과 후 execute 직전에 호출.
|
|
47
|
+
* 반환 = 분리 실행: invoke 는 handle 로 바로 resolve, execute 는 반환된 signal · ask · progress 로 계속 실행 후 settle.
|
|
48
|
+
* undefined = 동기 실행 (지금과 같음). span · onCommandError 는 분리 실행 전체를 덮는다
|
|
49
|
+
*/
|
|
50
|
+
detach?: (entry: RegisteredCommand) => Promise<DetachedExecution | undefined>;
|
|
51
|
+
}
|
|
52
|
+
/** detach 의 반환. execute 의 ctx 를 요청과 분리한다 (R2: 응답 종료 ≠ 취소) */
|
|
53
|
+
export interface DetachedExecution {
|
|
54
|
+
/** invoke 결과의 detached (MCP = CreateTaskResult) */
|
|
55
|
+
handle: unknown;
|
|
56
|
+
signal: AbortSignal;
|
|
57
|
+
ask: AskHandler;
|
|
58
|
+
progress: (update: ProgressUpdate) => void;
|
|
59
|
+
/** execute 종료. 값 = output 검증 후, 예외 = onCommandError 보고 후. 거부하지 않는다 */
|
|
60
|
+
settle(outcome: {
|
|
61
|
+
value: unknown;
|
|
62
|
+
} | {
|
|
63
|
+
error: unknown;
|
|
64
|
+
}): Promise<void>;
|
|
37
65
|
}
|
|
38
66
|
export interface InvokeResult {
|
|
39
67
|
/** 정식 ID (별칭으로 호출해도) */
|
|
40
68
|
command: string;
|
|
41
69
|
value: unknown;
|
|
70
|
+
/** detach 로 분리 실행됨: DetachedExecution.handle (value 는 undefined) */
|
|
71
|
+
detached?: unknown;
|
|
42
72
|
}
|
|
43
73
|
/**
|
|
44
74
|
* 어댑터 공통 실행기: ID 해석 (별칭 포함) → 권한 → (lazy 면 모듈 import) → Zod safeParse 1회 → 확인 (requiresConfirmation) → execute.
|
|
@@ -58,7 +88,15 @@ export declare function authorize(entry: RegisteredCommand, principal: Principal
|
|
|
58
88
|
}): void;
|
|
59
89
|
/** Zod safeParse 1회. 실패 = INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples) */
|
|
60
90
|
export declare function parseInput(entry: RegisteredCommand, rawInput: unknown): Promise<z.output<InputSchema>>;
|
|
91
|
+
/** output 검증 실패. MextError 가 아님 = 응답 EXECUTION_ERROR (message 만) · onCommandError 보고 */
|
|
92
|
+
export declare class OutputValidationError extends Error {
|
|
93
|
+
constructor(message: string);
|
|
94
|
+
}
|
|
61
95
|
/** ctx 생성. ask · progress · signal 은 어댑터 주입 */
|
|
62
96
|
export declare function createContext(entry: RegisteredCommand, options: InvokeOptions): CommandContext;
|
|
97
|
+
/** ctx.resourceUpdated uri: 비어 있지 않은 문자열. 아니면 TypeError (핸들러 버그) */
|
|
98
|
+
export declare function assertResourceUri(uri: unknown): asserts uri is string;
|
|
63
99
|
/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */
|
|
64
100
|
export declare function confirmationMessage(entry: RegisteredCommand, input: unknown): string;
|
|
101
|
+
/** INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples). 프롬프트 인자 (§12.3) 도 사용 */
|
|
102
|
+
export declare function invalidArgument(command: string, raw: unknown, zodIssues: readonly z.core.$ZodIssue[], expectedSchema: JsonSchema, examples: unknown[]): MextError<"INVALID_ARGUMENT">;
|
package/dist/invoke.js
CHANGED
|
@@ -12,7 +12,11 @@ import { loadCommand } from "./registry.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export async function invoke(registry, id, rawInput, options) {
|
|
14
14
|
const entry = findCommand(registry, id);
|
|
15
|
-
|
|
15
|
+
// span 의 취소 판정 signal. 분리 실행 후 = task signal
|
|
16
|
+
let signal = options.signal;
|
|
17
|
+
let handoff;
|
|
18
|
+
const handedOff = options.detach ? new Promise((resolve) => (handoff = resolve)) : undefined;
|
|
19
|
+
const run = observeCommand(entry.id, options, () => signal, async () => {
|
|
16
20
|
authorize(entry, options.principal, { readOnly: options.readOnly });
|
|
17
21
|
// lazy Registry (mext start) = 여기서 명령어 모듈 import. 권한 · ID 오류는 import 없이
|
|
18
22
|
const command = await loadCommand(entry);
|
|
@@ -26,8 +30,24 @@ export async function invoke(registry, id, rawInput, options) {
|
|
|
26
30
|
throw inputDeclined(entry.id, "confirm");
|
|
27
31
|
ctx.signal.throwIfAborted();
|
|
28
32
|
}
|
|
33
|
+
const detached = await options.detach?.(entry);
|
|
34
|
+
if (detached) {
|
|
35
|
+
signal = detached.signal;
|
|
36
|
+
handoff({ command: entry.id, value: undefined, detached: detached.handle });
|
|
37
|
+
const taskCtx = createContext(entry, { ...options, signal: detached.signal, ask: detached.ask, progress: detached.progress });
|
|
38
|
+
try {
|
|
39
|
+
const value = await parseOutput(entry.id, command, await command.execute(input, taskCtx));
|
|
40
|
+
await detached.settle({ value });
|
|
41
|
+
return { command: entry.id, value };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
await reportCommandError(error, entry.id, options, taskCtx.signal);
|
|
45
|
+
await detached.settle({ error });
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
29
49
|
try {
|
|
30
|
-
const value = await command.execute(input, ctx);
|
|
50
|
+
const value = await parseOutput(entry.id, command, await command.execute(input, ctx));
|
|
31
51
|
return { command: entry.id, value };
|
|
32
52
|
}
|
|
33
53
|
catch (error) {
|
|
@@ -35,6 +55,8 @@ export async function invoke(registry, id, rawInput, options) {
|
|
|
35
55
|
throw error;
|
|
36
56
|
}
|
|
37
57
|
});
|
|
58
|
+
// 분리 실행 = handoff 로 먼저 resolve. 이후 run 의 거부는 race 가 받는다 (미처리 거부 없음)
|
|
59
|
+
return handedOff ? Promise.race([handedOff, run]) : run;
|
|
38
60
|
}
|
|
39
61
|
/** ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage). 구현 = lookup.ts (help · 고객 CLI 공용) */
|
|
40
62
|
export { findCommand };
|
|
@@ -67,18 +89,47 @@ export async function parseInput(entry, rawInput) {
|
|
|
67
89
|
return result.data;
|
|
68
90
|
throw invalidArgument(entry.id, raw, result.error.issues, entry.inputSchema, [...entry.command.examples]);
|
|
69
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* output 이 있으면 반환값 safeParse (모든 어댑터 · 노출 방식, §12.2). 결과 = 파싱된 값 (outputSchema 와 같은 모양).
|
|
94
|
+
* 실패 = OutputValidationError (핸들러 예외와 같이 EXECUTION_ERROR · onCommandError 보고). 던진 오류는 검증하지 않는다
|
|
95
|
+
*/
|
|
96
|
+
async function parseOutput(id, command, value) {
|
|
97
|
+
if (!command.output)
|
|
98
|
+
return value;
|
|
99
|
+
const result = await command.output.safeParseAsync(value);
|
|
100
|
+
if (result.success)
|
|
101
|
+
return result.data;
|
|
102
|
+
const summary = result.error.issues
|
|
103
|
+
.map((i) => (i.path.length > 0 ? `${i.path.map(String).join(".")}: ${i.message}` : i.message))
|
|
104
|
+
.join("; ");
|
|
105
|
+
throw new OutputValidationError(`${id} returned a value that does not match its output schema: ${summary}`);
|
|
106
|
+
}
|
|
107
|
+
/** output 검증 실패. MextError 가 아님 = 응답 EXECUTION_ERROR (message 만) · onCommandError 보고 */
|
|
108
|
+
export class OutputValidationError extends Error {
|
|
109
|
+
constructor(message) {
|
|
110
|
+
super(message);
|
|
111
|
+
this.name = "OutputValidationError";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
70
114
|
/** ctx 생성. ask · progress · signal 은 어댑터 주입 */
|
|
71
115
|
export function createContext(entry, options) {
|
|
72
116
|
const handler = options.ask;
|
|
73
117
|
const ask = async (request) => {
|
|
118
|
+
// url 형식 오류 = 핸들러 버그 (TypeError → EXECUTION_ERROR). 실행 환경과 무관하게 먼저 검사
|
|
119
|
+
if (request.kind === "url")
|
|
120
|
+
assertAskUrl(request.url);
|
|
74
121
|
if (!handler) {
|
|
75
122
|
throw new MextError("INPUT_UNAVAILABLE", `${entry.id} requested input, but this environment cannot ask.`, {
|
|
76
123
|
command: entry.id,
|
|
124
|
+
...(request.kind === "url" ? { url: request.url } : {}),
|
|
77
125
|
});
|
|
78
126
|
}
|
|
79
127
|
if (request.kind === "confirm") {
|
|
80
128
|
return (await handler({ command: entry.id, kind: "confirm", message: request.message })) === true;
|
|
81
129
|
}
|
|
130
|
+
if (request.kind === "url") {
|
|
131
|
+
return (await handler({ command: entry.id, kind: "url", message: request.message, url: request.url })) === true;
|
|
132
|
+
}
|
|
82
133
|
const requestedSchema = z.toJSONSchema(request.schema, { target: "draft-2020-12", io: "input" });
|
|
83
134
|
const response = await handler({
|
|
84
135
|
command: entry.id,
|
|
@@ -98,8 +149,25 @@ export function createContext(entry, options) {
|
|
|
98
149
|
progress: options.progress ?? (() => { }),
|
|
99
150
|
ask: ask,
|
|
100
151
|
app: options.app ?? {},
|
|
152
|
+
resourceUpdated: options.resourceUpdated ?? noResourceUpdates,
|
|
101
153
|
};
|
|
102
154
|
}
|
|
155
|
+
/** state 없는 실행 (로컬 CLI · stateless 서버). uri 형식 오류만 TypeError */
|
|
156
|
+
async function noResourceUpdates(uri) {
|
|
157
|
+
assertResourceUri(uri);
|
|
158
|
+
}
|
|
159
|
+
/** ctx.resourceUpdated uri: 비어 있지 않은 문자열. 아니면 TypeError (핸들러 버그) */
|
|
160
|
+
export function assertResourceUri(uri) {
|
|
161
|
+
if (typeof uri !== "string" || uri === "")
|
|
162
|
+
throw new TypeError(`resourceUpdated uri must be a non-empty string: ${JSON.stringify(uri)}`);
|
|
163
|
+
}
|
|
164
|
+
/** ctx.ask url: http(s) 절대 URL. 아니면 TypeError */
|
|
165
|
+
function assertAskUrl(url) {
|
|
166
|
+
const parsed = typeof url === "string" && URL.canParse(url) ? new URL(url) : undefined;
|
|
167
|
+
if (parsed?.protocol !== "http:" && parsed?.protocol !== "https:") {
|
|
168
|
+
throw new TypeError(`ctx.ask url must be an absolute http(s) URL: ${JSON.stringify(url)}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
103
171
|
/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */
|
|
104
172
|
export function confirmationMessage(entry, input) {
|
|
105
173
|
let args;
|
|
@@ -111,7 +179,8 @@ export function confirmationMessage(entry, input) {
|
|
|
111
179
|
}
|
|
112
180
|
return `Run ${entry.id} with ${args}?${entry.command.destructive ? " This command is destructive." : ""}`;
|
|
113
181
|
}
|
|
114
|
-
|
|
182
|
+
/** INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples). 프롬프트 인자 (§12.3) 도 사용 */
|
|
183
|
+
export function invalidArgument(command, raw, zodIssues, expectedSchema, examples) {
|
|
115
184
|
const issues = zodIssues.map((i) => ({
|
|
116
185
|
path: i.path.map(String).join("."),
|
|
117
186
|
code: i.code,
|
package/dist/invoke.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.js","sourceRoot":"","sources":["../src/invoke.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from \"zod\"\nimport type { Ask, AskRequest, CommandContext, InputSchema, MextApp, ProgressUpdate } from \"./command.ts\"\nimport { inputDeclined, MextError, type ArgumentIssue } from \"./errors.ts\"\nimport { observeCommand, reportCommandError, type Instrumentation, type Transport } from \"./instrumentation.ts\"\nimport { findCommand } from \"./lookup.ts\"\nimport { hasScope, type Principal } from \"./principal.ts\"\nimport { loadCommand, type JsonSchema, type RegisteredCommand, type Registry } from \"./registry.ts\"\n\n/** 어댑터가 주입하는 ctx.ask 구현. command = 정식 ID */\nexport type AskHandler = (request: AskHandlerRequest) => Promise<unknown>\n\nexport type AskHandlerRequest =\n | { command: string; kind: \"confirm\"; message: string }\n | { command: string; kind: \"input\"; message: string; schema: InputSchema; requestedSchema: JsonSchema }\n\nexport interface InvokeOptions {\n principal: Principal\n /** 기본 = abort 되지 않는 signal */\n signal?: AbortSignal\n /** 기본 = 무시 */\n progress?: (update: ProgressUpdate) => void\n /** 기본 = ctx.ask 호출 시 INPUT_UNAVAILABLE */\n ask?: AskHandler\n app?: MextApp\n /** readOnly 명령어만 허용 (split 의 query 도구). 그 외 = PERMISSION_DENIED */\n readOnly?: boolean\n /** loadInstrumentation 결과. onCommandError · mext.command span. 기본 = 없음 */\n instrumentation?: Instrumentation\n /** 실행 어댑터 (onCommandError · span 속성). 기본 \"local\" */\n transport?: Transport\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate). span 의 부모 */\n traceContext?: Record<string, unknown>\n}\n\nexport interface InvokeResult {\n /** 정식 ID (별칭으로 호출해도) */\n command: string\n value: unknown\n}\n\n/**\n * 어댑터 공통 실행기: ID 해석 (별칭 포함) → 권한 → (lazy 면 모듈 import) → Zod safeParse 1회 → 확인 (requiresConfirmation) → execute.\n * 실패는 MextError (UNKNOWN_COMMAND · PERMISSION_DENIED · INVALID_ARGUMENT · INPUT_UNAVAILABLE · INPUT_DECLINED).\n * 핸들러 예외는 그대로 전파 (instrumentation 있으면 onCommandError 보고 후).\n * instrumentation.otel 있으면 findCommand 뒤 전체를 `mext.command <id>` span 으로 감싼다.\n */\nexport async function invoke(\n registry: Registry,\n id: string,\n rawInput: unknown,\n options: InvokeOptions,\n): Promise<InvokeResult> {\n const entry = findCommand(registry, id)\n return observeCommand(entry.id, options, () => options.signal, async () => {\n authorize(entry, options.principal, { readOnly: options.readOnly })\n // lazy Registry (mext start) = 여기서 명령어 모듈 import. 권한 · ID 오류는 import 없이\n const command = await loadCommand(entry)\n const input = await parseInput(entry, rawInput)\n const ctx = createContext(entry, options)\n ctx.signal.throwIfAborted()\n if (entry.command.requiresConfirmation) {\n // 확인은 ctx.ask 경유 (CLI = 프롬프트 · --yes, MCP = MRTR). ask 미주입 = INPUT_UNAVAILABLE\n const confirmed = await ctx.ask({ kind: \"confirm\", message: confirmationMessage(entry, input) })\n if (!confirmed) throw inputDeclined(entry.id, \"confirm\")\n ctx.signal.throwIfAborted()\n }\n try {\n const value = await command.execute(input, ctx)\n return { command: entry.id, value }\n } catch (error) {\n await reportCommandError(error, entry.id, options, ctx.signal)\n throw error\n }\n })\n}\n\n/** ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage). 구현 = lookup.ts (help · 고객 CLI 공용) */\nexport { findCommand }\n\n/**\n * 안전성 메타데이터를 주체에 대해 강제.\n * readOnly 전용 호출 = readOnly 명령어만 · anonymous = readOnly 명령어만 · command.scopes 전부 보유 (\"*\" = 전체).\n */\nexport function authorize(entry: RegisteredCommand, principal: Principal, options: { readOnly?: boolean } = {}): void {\n const c = entry.command\n const deny = (reason: \"anonymous\" | \"read_only\" | \"scope\", message: string): never =>\n throwError(\n new MextError(\"PERMISSION_DENIED\", message, {\n command: entry.id,\n reason,\n required_scopes: [...c.scopes],\n held_scopes: [...principal.scopes],\n }),\n )\n if (options.readOnly && !c.readOnly) deny(\"read_only\", `${entry.id} is not read-only and cannot run here.`)\n if (principal.kind === \"anonymous\" && !c.readOnly) {\n deny(\"anonymous\", `${entry.id} is not read-only and requires an authenticated principal.`)\n }\n const missing = c.scopes.filter((s) => !hasScope(principal, s))\n if (missing.length > 0) deny(\"scope\", `${entry.id} requires scopes: ${missing.join(\", \")}.`)\n}\n\n/** Zod safeParse 1회. 실패 = INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples) */\nexport async function parseInput(entry: RegisteredCommand, rawInput: unknown): Promise<z.output<InputSchema>> {\n const raw = rawInput === undefined ? {} : rawInput\n const result = await (await loadCommand(entry)).input.safeParseAsync(raw)\n if (result.success) return result.data\n throw invalidArgument(entry.id, raw, result.error.issues, entry.inputSchema, [...entry.command.examples])\n}\n\n/** ctx 생성. ask · progress · signal 은 어댑터 주입 */\nexport function createContext(entry: RegisteredCommand, options: InvokeOptions): CommandContext {\n const handler = options.ask\n const ask = async (request: AskRequest): Promise<unknown> => {\n if (!handler) {\n throw new MextError(\"INPUT_UNAVAILABLE\", `${entry.id} requested input, but this environment cannot ask.`, {\n command: entry.id,\n })\n }\n if (request.kind === \"confirm\") {\n return (await handler({ command: entry.id, kind: \"confirm\", message: request.message })) === true\n }\n const requestedSchema = z.toJSONSchema(request.schema, { target: \"draft-2020-12\", io: \"input\" }) as JsonSchema\n const response = await handler({\n command: entry.id,\n kind: \"input\",\n message: request.message,\n schema: request.schema,\n requestedSchema,\n })\n const parsed = await request.schema.safeParseAsync(response)\n if (parsed.success) return parsed.data\n throw invalidArgument(entry.id, response, parsed.error.issues, requestedSchema, [])\n }\n return {\n principal: options.principal,\n signal: options.signal ?? new AbortController().signal,\n progress: options.progress ?? (() => {}),\n ask: ask as Ask,\n app: options.app ?? ({} as MextApp),\n }\n}\n\n/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */\nexport function confirmationMessage(entry: RegisteredCommand, input: unknown): string {\n let args: string\n try {\n args = JSON.stringify(input) ?? \"{}\"\n } catch {\n args = \"{}\"\n }\n return `Run ${entry.id} with ${args}?${entry.command.destructive ? \" This command is destructive.\" : \"\"}`\n}\n\nfunction invalidArgument(\n command: string,\n raw: unknown,\n zodIssues: readonly z.core.$ZodIssue[],\n expectedSchema: JsonSchema,\n examples: unknown[],\n): MextError<\"INVALID_ARGUMENT\"> {\n const issues: ArgumentIssue[] = zodIssues.map((i) => ({\n path: i.path.map(String).join(\".\"),\n code: i.code,\n message: i.message,\n }))\n const missing = new Set<string>()\n for (const i of zodIssues) {\n if (i.code === \"invalid_type\" && i.path.length > 0 && isAbsent(raw, i.path)) missing.add(i.path.map(String).join(\".\"))\n }\n const missing_fields = [...missing]\n const summary =\n missing_fields.length > 0\n ? `missing required: ${missing_fields.join(\", \")}`\n : issues.map((i) => (i.path ? `${i.path}: ${i.message}` : i.message)).join(\"; \")\n return new MextError(\"INVALID_ARGUMENT\", `Invalid arguments for ${command}: ${summary}`, {\n command,\n missing_fields,\n issues,\n expected_schema: expectedSchema,\n examples,\n })\n}\n\n/** 원본 입력에 경로가 없는지 (값 undefined 포함) */\nfunction isAbsent(raw: unknown, path: readonly PropertyKey[]): boolean {\n let cur: unknown = raw\n for (const key of path) {\n if (cur === null || typeof cur !== \"object\") return true\n cur = (cur as Record<PropertyKey, unknown>)[key]\n if (cur === undefined) return true\n }\n return false\n}\n\nfunction throwError(error: MextError): never {\n throw error\n}\n"]}
|
|
1
|
+
{"version":3,"file":"invoke.js","sourceRoot":"","sources":["../src/invoke.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from \"zod\"\nimport type { Ask, AskRequest, Command, CommandContext, InputSchema, MextApp, ProgressUpdate } from \"./command.ts\"\nimport { inputDeclined, MextError, type ArgumentIssue } from \"./errors.ts\"\nimport { observeCommand, reportCommandError, type Instrumentation, type Transport } from \"./instrumentation.ts\"\nimport { findCommand } from \"./lookup.ts\"\nimport { hasScope, type Principal } from \"./principal.ts\"\nimport { loadCommand, type JsonSchema, type RegisteredCommand, type Registry } from \"./registry.ts\"\n\n/** 어댑터가 주입하는 ctx.ask 구현. command = 정식 ID */\nexport type AskHandler = (request: AskHandlerRequest) => Promise<unknown>\n\nexport type AskHandlerRequest =\n | { command: string; kind: \"confirm\"; message: string }\n | { command: string; kind: \"input\"; message: string; schema: InputSchema; requestedSchema: JsonSchema }\n | { command: string; kind: \"url\"; message: string; url: string }\n\nexport interface InvokeOptions {\n principal: Principal\n /** 기본 = abort 되지 않는 signal */\n signal?: AbortSignal\n /** 기본 = 무시 */\n progress?: (update: ProgressUpdate) => void\n /** 기본 = ctx.ask 호출 시 INPUT_UNAVAILABLE */\n ask?: AskHandler\n app?: MextApp\n /** readOnly 명령어만 허용 (split 의 query 도구). 그 외 = PERMISSION_DENIED */\n readOnly?: boolean\n /** loadInstrumentation 결과. onCommandError · mext.command span. 기본 = 없음 */\n instrumentation?: Instrumentation\n /** 실행 어댑터 (onCommandError · span 속성). 기본 \"local\" */\n transport?: Transport\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate). span 의 부모 */\n traceContext?: Record<string, unknown>\n /** ctx.resourceUpdated (§12.5). 기본 = no-op */\n resourceUpdated?: (uri: string) => Promise<void>\n /**\n * tasks 확장 (§12.6). 입력 검증 · 권한 · requiresConfirmation 통과 후 execute 직전에 호출.\n * 반환 = 분리 실행: invoke 는 handle 로 바로 resolve, execute 는 반환된 signal · ask · progress 로 계속 실행 후 settle.\n * undefined = 동기 실행 (지금과 같음). span · onCommandError 는 분리 실행 전체를 덮는다\n */\n detach?: (entry: RegisteredCommand) => Promise<DetachedExecution | undefined>\n}\n\n/** detach 의 반환. execute 의 ctx 를 요청과 분리한다 (R2: 응답 종료 ≠ 취소) */\nexport interface DetachedExecution {\n /** invoke 결과의 detached (MCP = CreateTaskResult) */\n handle: unknown\n signal: AbortSignal\n ask: AskHandler\n progress: (update: ProgressUpdate) => void\n /** execute 종료. 값 = output 검증 후, 예외 = onCommandError 보고 후. 거부하지 않는다 */\n settle(outcome: { value: unknown } | { error: unknown }): Promise<void>\n}\n\nexport interface InvokeResult {\n /** 정식 ID (별칭으로 호출해도) */\n command: string\n value: unknown\n /** detach 로 분리 실행됨: DetachedExecution.handle (value 는 undefined) */\n detached?: unknown\n}\n\n/**\n * 어댑터 공통 실행기: ID 해석 (별칭 포함) → 권한 → (lazy 면 모듈 import) → Zod safeParse 1회 → 확인 (requiresConfirmation) → execute.\n * 실패는 MextError (UNKNOWN_COMMAND · PERMISSION_DENIED · INVALID_ARGUMENT · INPUT_UNAVAILABLE · INPUT_DECLINED).\n * 핸들러 예외는 그대로 전파 (instrumentation 있으면 onCommandError 보고 후).\n * instrumentation.otel 있으면 findCommand 뒤 전체를 `mext.command <id>` span 으로 감싼다.\n */\nexport async function invoke(\n registry: Registry,\n id: string,\n rawInput: unknown,\n options: InvokeOptions,\n): Promise<InvokeResult> {\n const entry = findCommand(registry, id)\n // span 의 취소 판정 signal. 분리 실행 후 = task signal\n let signal = options.signal\n let handoff: ((result: InvokeResult) => void) | undefined\n const handedOff = options.detach ? new Promise<InvokeResult>((resolve) => (handoff = resolve)) : undefined\n const run = observeCommand(entry.id, options, () => signal, async () => {\n authorize(entry, options.principal, { readOnly: options.readOnly })\n // lazy Registry (mext start) = 여기서 명령어 모듈 import. 권한 · ID 오류는 import 없이\n const command = await loadCommand(entry)\n const input = await parseInput(entry, rawInput)\n const ctx = createContext(entry, options)\n ctx.signal.throwIfAborted()\n if (entry.command.requiresConfirmation) {\n // 확인은 ctx.ask 경유 (CLI = 프롬프트 · --yes, MCP = MRTR). ask 미주입 = INPUT_UNAVAILABLE\n const confirmed = await ctx.ask({ kind: \"confirm\", message: confirmationMessage(entry, input) })\n if (!confirmed) throw inputDeclined(entry.id, \"confirm\")\n ctx.signal.throwIfAborted()\n }\n const detached = await options.detach?.(entry)\n if (detached) {\n signal = detached.signal\n handoff!({ command: entry.id, value: undefined, detached: detached.handle })\n const taskCtx = createContext(entry, { ...options, signal: detached.signal, ask: detached.ask, progress: detached.progress })\n try {\n const value = await parseOutput(entry.id, command, await command.execute(input, taskCtx))\n await detached.settle({ value })\n return { command: entry.id, value }\n } catch (error) {\n await reportCommandError(error, entry.id, options, taskCtx.signal)\n await detached.settle({ error })\n throw error\n }\n }\n try {\n const value = await parseOutput(entry.id, command, await command.execute(input, ctx))\n return { command: entry.id, value }\n } catch (error) {\n await reportCommandError(error, entry.id, options, ctx.signal)\n throw error\n }\n })\n // 분리 실행 = handoff 로 먼저 resolve. 이후 run 의 거부는 race 가 받는다 (미처리 거부 없음)\n return handedOff ? Promise.race([handedOff, run]) : run\n}\n\n/** ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage). 구현 = lookup.ts (help · 고객 CLI 공용) */\nexport { findCommand }\n\n/**\n * 안전성 메타데이터를 주체에 대해 강제.\n * readOnly 전용 호출 = readOnly 명령어만 · anonymous = readOnly 명령어만 · command.scopes 전부 보유 (\"*\" = 전체).\n */\nexport function authorize(entry: RegisteredCommand, principal: Principal, options: { readOnly?: boolean } = {}): void {\n const c = entry.command\n const deny = (reason: \"anonymous\" | \"read_only\" | \"scope\", message: string): never =>\n throwError(\n new MextError(\"PERMISSION_DENIED\", message, {\n command: entry.id,\n reason,\n required_scopes: [...c.scopes],\n held_scopes: [...principal.scopes],\n }),\n )\n if (options.readOnly && !c.readOnly) deny(\"read_only\", `${entry.id} is not read-only and cannot run here.`)\n if (principal.kind === \"anonymous\" && !c.readOnly) {\n deny(\"anonymous\", `${entry.id} is not read-only and requires an authenticated principal.`)\n }\n const missing = c.scopes.filter((s) => !hasScope(principal, s))\n if (missing.length > 0) deny(\"scope\", `${entry.id} requires scopes: ${missing.join(\", \")}.`)\n}\n\n/** Zod safeParse 1회. 실패 = INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples) */\nexport async function parseInput(entry: RegisteredCommand, rawInput: unknown): Promise<z.output<InputSchema>> {\n const raw = rawInput === undefined ? {} : rawInput\n const result = await (await loadCommand(entry)).input.safeParseAsync(raw)\n if (result.success) return result.data\n throw invalidArgument(entry.id, raw, result.error.issues, entry.inputSchema, [...entry.command.examples])\n}\n\n/**\n * output 이 있으면 반환값 safeParse (모든 어댑터 · 노출 방식, §12.2). 결과 = 파싱된 값 (outputSchema 와 같은 모양).\n * 실패 = OutputValidationError (핸들러 예외와 같이 EXECUTION_ERROR · onCommandError 보고). 던진 오류는 검증하지 않는다\n */\nasync function parseOutput(id: string, command: Command, value: unknown): Promise<unknown> {\n if (!command.output) return value\n const result = await command.output.safeParseAsync(value)\n if (result.success) return result.data\n const summary = result.error.issues\n .map((i) => (i.path.length > 0 ? `${i.path.map(String).join(\".\")}: ${i.message}` : i.message))\n .join(\"; \")\n throw new OutputValidationError(`${id} returned a value that does not match its output schema: ${summary}`)\n}\n\n/** output 검증 실패. MextError 가 아님 = 응답 EXECUTION_ERROR (message 만) · onCommandError 보고 */\nexport class OutputValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"OutputValidationError\"\n }\n}\n\n/** ctx 생성. ask · progress · signal 은 어댑터 주입 */\nexport function createContext(entry: RegisteredCommand, options: InvokeOptions): CommandContext {\n const handler = options.ask\n const ask = async (request: AskRequest): Promise<unknown> => {\n // url 형식 오류 = 핸들러 버그 (TypeError → EXECUTION_ERROR). 실행 환경과 무관하게 먼저 검사\n if (request.kind === \"url\") assertAskUrl(request.url)\n if (!handler) {\n throw new MextError(\"INPUT_UNAVAILABLE\", `${entry.id} requested input, but this environment cannot ask.`, {\n command: entry.id,\n ...(request.kind === \"url\" ? { url: request.url } : {}),\n })\n }\n if (request.kind === \"confirm\") {\n return (await handler({ command: entry.id, kind: \"confirm\", message: request.message })) === true\n }\n if (request.kind === \"url\") {\n return (await handler({ command: entry.id, kind: \"url\", message: request.message, url: request.url })) === true\n }\n const requestedSchema = z.toJSONSchema(request.schema, { target: \"draft-2020-12\", io: \"input\" }) as JsonSchema\n const response = await handler({\n command: entry.id,\n kind: \"input\",\n message: request.message,\n schema: request.schema,\n requestedSchema,\n })\n const parsed = await request.schema.safeParseAsync(response)\n if (parsed.success) return parsed.data\n throw invalidArgument(entry.id, response, parsed.error.issues, requestedSchema, [])\n }\n return {\n principal: options.principal,\n signal: options.signal ?? new AbortController().signal,\n progress: options.progress ?? (() => {}),\n ask: ask as Ask,\n app: options.app ?? ({} as MextApp),\n resourceUpdated: options.resourceUpdated ?? noResourceUpdates,\n }\n}\n\n/** state 없는 실행 (로컬 CLI · stateless 서버). uri 형식 오류만 TypeError */\nasync function noResourceUpdates(uri: string): Promise<void> {\n assertResourceUri(uri)\n}\n\n/** ctx.resourceUpdated uri: 비어 있지 않은 문자열. 아니면 TypeError (핸들러 버그) */\nexport function assertResourceUri(uri: unknown): asserts uri is string {\n if (typeof uri !== \"string\" || uri === \"\") throw new TypeError(`resourceUpdated uri must be a non-empty string: ${JSON.stringify(uri)}`)\n}\n\n/** ctx.ask url: http(s) 절대 URL. 아니면 TypeError */\nfunction assertAskUrl(url: unknown): asserts url is string {\n const parsed = typeof url === \"string\" && URL.canParse(url) ? new URL(url) : undefined\n if (parsed?.protocol !== \"http:\" && parsed?.protocol !== \"https:\") {\n throw new TypeError(`ctx.ask url must be an absolute http(s) URL: ${JSON.stringify(url)}`)\n }\n}\n\n/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */\nexport function confirmationMessage(entry: RegisteredCommand, input: unknown): string {\n let args: string\n try {\n args = JSON.stringify(input) ?? \"{}\"\n } catch {\n args = \"{}\"\n }\n return `Run ${entry.id} with ${args}?${entry.command.destructive ? \" This command is destructive.\" : \"\"}`\n}\n\n/** INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples). 프롬프트 인자 (§12.3) 도 사용 */\nexport function invalidArgument(\n command: string,\n raw: unknown,\n zodIssues: readonly z.core.$ZodIssue[],\n expectedSchema: JsonSchema,\n examples: unknown[],\n): MextError<\"INVALID_ARGUMENT\"> {\n const issues: ArgumentIssue[] = zodIssues.map((i) => ({\n path: i.path.map(String).join(\".\"),\n code: i.code,\n message: i.message,\n }))\n const missing = new Set<string>()\n for (const i of zodIssues) {\n if (i.code === \"invalid_type\" && i.path.length > 0 && isAbsent(raw, i.path)) missing.add(i.path.map(String).join(\".\"))\n }\n const missing_fields = [...missing]\n const summary =\n missing_fields.length > 0\n ? `missing required: ${missing_fields.join(\", \")}`\n : issues.map((i) => (i.path ? `${i.path}: ${i.message}` : i.message)).join(\"; \")\n return new MextError(\"INVALID_ARGUMENT\", `Invalid arguments for ${command}: ${summary}`, {\n command,\n missing_fields,\n issues,\n expected_schema: expectedSchema,\n examples,\n })\n}\n\n/** 원본 입력에 경로가 없는지 (값 undefined 포함) */\nfunction isAbsent(raw: unknown, path: readonly PropertyKey[]): boolean {\n let cur: unknown = raw\n for (const key of path) {\n if (cur === null || typeof cur !== \"object\") return true\n cur = (cur as Record<PropertyKey, unknown>)[key]\n if (cur === undefined) return true\n }\n return false\n}\n\nfunction throwError(error: MextError): never {\n throw error\n}\n"]}
|
package/dist/local-bin.js
CHANGED
|
@@ -8,10 +8,9 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
8
8
|
};
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { pathToFileURL } from "node:url";
|
|
11
|
-
import { cliSpecFromJsonSchema, defineCliSpec } from "./cli/args.js";
|
|
12
11
|
import { table } from "./cli/help.js";
|
|
13
12
|
import { resolveIo } from "./cli/io.js";
|
|
14
|
-
import { EXIT } from "./cli/output.js";
|
|
13
|
+
import { emitError, EXIT } from "./cli/output.js";
|
|
15
14
|
import { runLocalCommand } from "./cli/run.js";
|
|
16
15
|
import { mcpInstallText } from "./mcp/install.js";
|
|
17
16
|
import { loadBuild, startFromBuild } from "./start.js";
|
|
@@ -38,7 +37,7 @@ export async function mainLocalBin(dir, options) {
|
|
|
38
37
|
const code = await runLocalBin(dir, options, process.argv.slice(2), { signal: controller.signal });
|
|
39
38
|
process.stdout.write("", () => process.stderr.write("", () => process.exit(code)));
|
|
40
39
|
}
|
|
41
|
-
/** .mext/ → 로컬 실행 코어 입력. CLI 필드 =
|
|
40
|
+
/** .mext/ → 로컬 실행 코어 입력. CLI 필드 = 번들된 명령어의 Zod 입력 분석 (mext run 과 같음) */
|
|
42
41
|
function buildSource(dir, options) {
|
|
43
42
|
return {
|
|
44
43
|
prog: options.bin,
|
|
@@ -47,9 +46,6 @@ function buildSource(dir, options) {
|
|
|
47
46
|
const build = await loadBuild(dir);
|
|
48
47
|
if (options.errors !== undefined)
|
|
49
48
|
setFormatter(await loadFormatter(dir, options.errors));
|
|
50
|
-
for (const entry of build.registry.commands.values()) {
|
|
51
|
-
defineCliSpec(entry, cliSpecFromJsonSchema(entry.inputSchema, entry.command.positional));
|
|
52
|
-
}
|
|
53
49
|
return { instrumentation: build.instrumentation, config: build.config, registry: build.registry };
|
|
54
50
|
},
|
|
55
51
|
};
|
|
@@ -78,14 +74,26 @@ async function mcp(dir, options, argv, io) {
|
|
|
78
74
|
return EXIT.ok;
|
|
79
75
|
}
|
|
80
76
|
if (sub === "serve" && rest.length === 0) {
|
|
81
|
-
|
|
77
|
+
// 종료 = SIGINT (io.signal) · SIGTERM → close → cleanup 후 exit 0 (mext start --stdio 와 같음)
|
|
78
|
+
const term = new AbortController();
|
|
79
|
+
const onTerm = () => term.abort();
|
|
80
|
+
process.once("SIGTERM", onTerm);
|
|
81
|
+
try {
|
|
82
|
+
const signal = io.signal ? AbortSignal.any([io.signal, term.signal]) : term.signal;
|
|
83
|
+
return await startFromBuild(dir, { argv: ["--stdio"], stderr: io.stderr, signal });
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
process.off("SIGTERM", onTerm);
|
|
87
|
+
}
|
|
82
88
|
}
|
|
83
89
|
if (sub === "install" && rest.length === 0) {
|
|
84
90
|
io.stdout.write(`${mcpInstallText({ name: bin, url: false, stdioCommand: `${bin} mcp serve` })}\n`);
|
|
85
91
|
return EXIT.ok;
|
|
86
92
|
}
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
// 사용법 오류 = INVALID_ARGUMENT. cli.errors 적용 (명령어 실행과 같은 formatter)
|
|
94
|
+
const message = sub === undefined ? "missing subcommand" : `unknown argument "${argv.join(" ")}"`;
|
|
95
|
+
const formatter = options.errors === undefined ? undefined : await loadFormatter(dir, options.errors);
|
|
96
|
+
return emitError(io, { body: { code: "INVALID_ARGUMENT", message }, text: `error: ${message}\n\n${usage}`, exitCode: EXIT.usage, json: false }, formatter);
|
|
89
97
|
}
|
|
90
98
|
/** 사람용 루트 help 뒤의 내장 명령 */
|
|
91
99
|
function builtinHelp(bin) {
|
package/dist/local-bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-bin.js","sourceRoot":"","sources":["../src/local-bin.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { cliSpecFromJsonSchema, defineCliSpec } from \"./cli/args.ts\"\nimport { table } from \"./cli/help.ts\"\nimport { resolveIo, type CliIo } from \"./cli/io.ts\"\nimport { EXIT, type CliErrorFormatter } from \"./cli/output.ts\"\nimport { runLocalCommand, type LocalRunSource } from \"./cli/run.ts\"\nimport { mcpInstallText } from \"./mcp/install.ts\"\nimport { loadBuild, startFromBuild } from \"./start.ts\"\n\n/**\n * 로컬 bin (cli.local, §5). .mext/bin.js 가 호출하는 런타임 (@obov/mext/start).\n * 생성 파일 계약 (mext build 가 쓰는 bin.js, 바꾸면 기존 빌드가 깨진다):\n * await mainLocalBin(new URL(\".\", import.meta.url), { bin, version, errors? })\n * 명령: <bin> help · <bin> <ns> <cmd> [flags] · <bin> mcp serve · <bin> mcp install · <bin> --version.\n * 명령어 실행 = mext run (로컬) 과 같은 코어 (cli/run.ts runLocalCommand), Registry = .mext/manifest.json (소스 · TypeScript 없음)\n */\n\nexport interface LocalBinOptions {\n /** 실행 파일 이름 (cli.bin) */\n bin: string\n /** --version (cli.version > 도입 프로젝트 package.json version) */\n version: string\n /** .mext/ 기준 cli.errors 번들 모듈 (예: \"cli-errors.js\") */\n errors?: string\n}\n\n/** 로컬 bin 실행. argv = 실행 파일 뒤 인자. 반환 = exit code */\nexport async function runLocalBin(\n dir: string | URL,\n options: LocalBinOptions,\n argv: readonly string[],\n partialIo: Partial<CliIo> = {},\n): Promise<number> {\n const io = resolveIo(partialIo)\n const [first, ...rest] = argv\n if (first === \"--version\" || first === \"-v\") {\n io.stdout.write(`${options.version}\\n`)\n return EXIT.ok\n }\n if (first === \"mcp\") return mcp(dir, options, rest, io)\n return runLocalCommand(argv, io, buildSource(dir, options))\n}\n\n/** 프로세스 진입점 (bin.js). 첫 SIGINT = ctx.signal abort (exit 130), 두 번째 = 즉시 종료 */\nexport async function mainLocalBin(dir: string | URL, options: LocalBinOptions): Promise<void> {\n const controller = new AbortController()\n process.on(\"SIGINT\", () => {\n if (controller.signal.aborted) process.exit(130)\n controller.abort()\n })\n const code = await runLocalBin(dir, options, process.argv.slice(2), { signal: controller.signal })\n process.stdout.write(\"\", () => process.stderr.write(\"\", () => process.exit(code)))\n}\n\n/** .mext/ → 로컬 실행 코어 입력. CLI 필드 = manifest 입력 스키마 (JSON Schema) 분석 (고객 CLI 와 같음) */\nfunction buildSource(dir: string | URL, options: LocalBinOptions): LocalRunSource {\n return {\n prog: options.bin,\n footer: builtinHelp(options.bin),\n async load(setFormatter) {\n const build = await loadBuild(dir)\n if (options.errors !== undefined) setFormatter(await loadFormatter(dir, options.errors))\n for (const entry of build.registry.commands.values()) {\n defineCliSpec(entry, cliSpecFromJsonSchema(entry.inputSchema, entry.command.positional))\n }\n return { instrumentation: build.instrumentation, config: build.config, registry: build.registry }\n },\n }\n}\n\nasync function loadFormatter(dir: string | URL, file: string): Promise<CliErrorFormatter> {\n const href = typeof dir === \"string\" ? pathToFileURL(resolve(dir, file)).href : new URL(file, dir.href.endsWith(\"/\") ? dir : `${dir.href}/`).href\n const mod = (await import(href)) as { default?: unknown }\n if (typeof mod.default !== \"function\") throw new TypeError(`${file}: default export must be a function (CliErrorFormatter)`)\n return mod.default as CliErrorFormatter\n}\n\nasync function mcp(dir: string | URL, options: LocalBinOptions, argv: readonly string[], io: CliIo): Promise<number> {\n const { bin } = options\n const [sub, ...rest] = argv\n const isHelp = (t: string): boolean => t === \"--help\" || t === \"-h\"\n const usage = [\n \"Usage:\",\n ...table([\n [`${bin} mcp serve`, \"Run the MCP server over stdio (handlers run on this machine)\"],\n [`${bin} mcp install`, \"Print MCP client registration commands (does not modify client config files)\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n if (sub !== undefined && (isHelp(sub) || rest.some(isHelp))) {\n io.stdout.write(usage)\n return EXIT.ok\n }\n if (sub === \"serve\" && rest.length === 0) {\n return startFromBuild(dir, { argv: [\"--stdio\"], stderr: io.stderr, ...(io.signal ? { signal: io.signal } : {}) })\n }\n if (sub === \"install\" && rest.length === 0) {\n io.stdout.write(`${mcpInstallText({ name: bin, url: false, stdioCommand: `${bin} mcp serve` })}\\n`)\n return EXIT.ok\n }\n io.stderr.write(`error: ${sub === undefined ? \"missing subcommand\" : `unknown argument \"${argv.join(\" \")}\"`}\\n\\n${usage}`)\n return EXIT.usage\n}\n\n/** 사람용 루트 help 뒤의 내장 명령 */\nfunction builtinHelp(bin: string): string {\n return [\n \"CLI commands:\",\n ...table([\n [\"mcp serve\", `MCP server over stdio (${bin} mcp install for registration)`],\n [\"mcp install\", \"Print MCP client registration commands\"],\n [\"--version\", \"Print the version\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n}\n"]}
|
|
1
|
+
{"version":3,"file":"local-bin.js","sourceRoot":"","sources":["../src/local-bin.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { table } from \"./cli/help.ts\"\nimport { resolveIo, type CliIo } from \"./cli/io.ts\"\nimport { emitError, EXIT, type CliErrorFormatter } from \"./cli/output.ts\"\nimport { runLocalCommand, type LocalRunSource } from \"./cli/run.ts\"\nimport { mcpInstallText } from \"./mcp/install.ts\"\nimport { loadBuild, startFromBuild } from \"./start.ts\"\n\n/**\n * 로컬 bin (cli.local, §5). .mext/bin.js 가 호출하는 런타임 (@obov/mext/start).\n * 생성 파일 계약 (mext build 가 쓰는 bin.js, 바꾸면 기존 빌드가 깨진다):\n * await mainLocalBin(new URL(\".\", import.meta.url), { bin, version, errors? })\n * 명령: <bin> help · <bin> <ns> <cmd> [flags] · <bin> mcp serve · <bin> mcp install · <bin> --version.\n * 명령어 실행 = mext run (로컬) 과 같은 코어 (cli/run.ts runLocalCommand), Registry = .mext/manifest.json (소스 · TypeScript 없음)\n */\n\nexport interface LocalBinOptions {\n /** 실행 파일 이름 (cli.bin) */\n bin: string\n /** --version (cli.version > 도입 프로젝트 package.json version) */\n version: string\n /** .mext/ 기준 cli.errors 번들 모듈 (예: \"cli-errors.js\") */\n errors?: string\n}\n\n/** 로컬 bin 실행. argv = 실행 파일 뒤 인자. 반환 = exit code */\nexport async function runLocalBin(\n dir: string | URL,\n options: LocalBinOptions,\n argv: readonly string[],\n partialIo: Partial<CliIo> = {},\n): Promise<number> {\n const io = resolveIo(partialIo)\n const [first, ...rest] = argv\n if (first === \"--version\" || first === \"-v\") {\n io.stdout.write(`${options.version}\\n`)\n return EXIT.ok\n }\n if (first === \"mcp\") return mcp(dir, options, rest, io)\n return runLocalCommand(argv, io, buildSource(dir, options))\n}\n\n/** 프로세스 진입점 (bin.js). 첫 SIGINT = ctx.signal abort (exit 130), 두 번째 = 즉시 종료 */\nexport async function mainLocalBin(dir: string | URL, options: LocalBinOptions): Promise<void> {\n const controller = new AbortController()\n process.on(\"SIGINT\", () => {\n if (controller.signal.aborted) process.exit(130)\n controller.abort()\n })\n const code = await runLocalBin(dir, options, process.argv.slice(2), { signal: controller.signal })\n process.stdout.write(\"\", () => process.stderr.write(\"\", () => process.exit(code)))\n}\n\n/** .mext/ → 로컬 실행 코어 입력. CLI 필드 = 번들된 명령어의 Zod 입력 분석 (mext run 과 같음) */\nfunction buildSource(dir: string | URL, options: LocalBinOptions): LocalRunSource {\n return {\n prog: options.bin,\n footer: builtinHelp(options.bin),\n async load(setFormatter) {\n const build = await loadBuild(dir)\n if (options.errors !== undefined) setFormatter(await loadFormatter(dir, options.errors))\n return { instrumentation: build.instrumentation, config: build.config, registry: build.registry }\n },\n }\n}\n\nasync function loadFormatter(dir: string | URL, file: string): Promise<CliErrorFormatter> {\n const href = typeof dir === \"string\" ? pathToFileURL(resolve(dir, file)).href : new URL(file, dir.href.endsWith(\"/\") ? dir : `${dir.href}/`).href\n const mod = (await import(href)) as { default?: unknown }\n if (typeof mod.default !== \"function\") throw new TypeError(`${file}: default export must be a function (CliErrorFormatter)`)\n return mod.default as CliErrorFormatter\n}\n\nasync function mcp(dir: string | URL, options: LocalBinOptions, argv: readonly string[], io: CliIo): Promise<number> {\n const { bin } = options\n const [sub, ...rest] = argv\n const isHelp = (t: string): boolean => t === \"--help\" || t === \"-h\"\n const usage = [\n \"Usage:\",\n ...table([\n [`${bin} mcp serve`, \"Run the MCP server over stdio (handlers run on this machine)\"],\n [`${bin} mcp install`, \"Print MCP client registration commands (does not modify client config files)\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n if (sub !== undefined && (isHelp(sub) || rest.some(isHelp))) {\n io.stdout.write(usage)\n return EXIT.ok\n }\n if (sub === \"serve\" && rest.length === 0) {\n // 종료 = SIGINT (io.signal) · SIGTERM → close → cleanup 후 exit 0 (mext start --stdio 와 같음)\n const term = new AbortController()\n const onTerm = (): void => term.abort()\n process.once(\"SIGTERM\", onTerm)\n try {\n const signal = io.signal ? AbortSignal.any([io.signal, term.signal]) : term.signal\n return await startFromBuild(dir, { argv: [\"--stdio\"], stderr: io.stderr, signal })\n } finally {\n process.off(\"SIGTERM\", onTerm)\n }\n }\n if (sub === \"install\" && rest.length === 0) {\n io.stdout.write(`${mcpInstallText({ name: bin, url: false, stdioCommand: `${bin} mcp serve` })}\\n`)\n return EXIT.ok\n }\n // 사용법 오류 = INVALID_ARGUMENT. cli.errors 적용 (명령어 실행과 같은 formatter)\n const message = sub === undefined ? \"missing subcommand\" : `unknown argument \"${argv.join(\" \")}\"`\n const formatter = options.errors === undefined ? undefined : await loadFormatter(dir, options.errors)\n return emitError(io, { body: { code: \"INVALID_ARGUMENT\", message }, text: `error: ${message}\\n\\n${usage}`, exitCode: EXIT.usage, json: false }, formatter)\n}\n\n/** 사람용 루트 help 뒤의 내장 명령 */\nfunction builtinHelp(bin: string): string {\n return [\n \"CLI commands:\",\n ...table([\n [\"mcp serve\", `MCP server over stdio (${bin} mcp install for registration)`],\n [\"mcp install\", \"Print MCP client registration commands\"],\n [\"--version\", \"Print the version\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n}\n"]}
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { ToolExposure } from "./mcp/dispatch.ts";
|
|
1
2
|
import type { JsonSchema, RegisteredCommand, Registry } from "./registry.ts";
|
|
3
|
+
import type { ManifestPrompt } from "./prompt.ts";
|
|
4
|
+
import type { ManifestResource } from "./resource.ts";
|
|
5
|
+
import type { RouteMethod } from "./route.ts";
|
|
2
6
|
export declare const MANIFEST_SCHEMA_VERSION = 1;
|
|
3
7
|
export interface ManifestCommand {
|
|
4
8
|
id: string;
|
|
@@ -16,6 +20,10 @@ export interface ManifestCommand {
|
|
|
16
20
|
/** JSON Schema 2020-12 */
|
|
17
21
|
inputSchema: JsonSchema;
|
|
18
22
|
outputSchema?: JsonSchema;
|
|
23
|
+
/** task 명령어 (§12.6). 없음 = 동기 명령어 */
|
|
24
|
+
task?: {
|
|
25
|
+
ttlMs: number;
|
|
26
|
+
};
|
|
19
27
|
}
|
|
20
28
|
export interface ManifestNamespace {
|
|
21
29
|
id: string;
|
|
@@ -41,6 +49,16 @@ export interface BuildManifestCommand extends ManifestCommand {
|
|
|
41
49
|
/** .mext/ 기준 상대 경로 (posix). commands/users/get.ts → "commands/users/get.js". default export = defineCommand 결과 */
|
|
42
50
|
module: string;
|
|
43
51
|
}
|
|
52
|
+
/** mext build 의 .mext/manifest.json 자원: 자원 메타데이터 + 번들 모듈 경로 */
|
|
53
|
+
export interface BuildManifestResource extends ManifestResource {
|
|
54
|
+
/** .mext/ 기준 상대 경로 (posix). resources/users/profile.ts → "resources/users/profile.js". default export = defineResource 결과 */
|
|
55
|
+
module: string;
|
|
56
|
+
}
|
|
57
|
+
/** mext build 의 .mext/manifest.json 프롬프트: 프롬프트 메타데이터 + 번들 모듈 경로 */
|
|
58
|
+
export interface BuildManifestPrompt extends ManifestPrompt {
|
|
59
|
+
/** .mext/ 기준 상대 경로 (posix). prompts/github/review.ts → "prompts/github/review.js". default export = definePrompt 결과 */
|
|
60
|
+
module: string;
|
|
61
|
+
}
|
|
44
62
|
/**
|
|
45
63
|
* .mext/manifest.json (mext build 산출, mext start · handler.js 가 읽는다).
|
|
46
64
|
* namespaces · description = Manifest 와 같음
|
|
@@ -50,19 +68,46 @@ export interface BuildManifest extends Omit<Manifest, "commands"> {
|
|
|
50
68
|
mextVersion: string;
|
|
51
69
|
/** 빌드 식별자 (D3: mextVersion · manifestHash · 번들 코드의 sha256 앞 16자, 같은 입력 = 같은 값) */
|
|
52
70
|
buildId: string;
|
|
53
|
-
/** manifestHash(commands) */
|
|
71
|
+
/** manifestHash(commands, { resources: 자원 uri, prompts: 이름 · argsSchema }) */
|
|
54
72
|
manifestHash: string;
|
|
73
|
+
/** MCP 도구 노출 방식 (mext.config.ts tools, 기본 execute). 고객 CLI 번들 형태의 호출 방식 (§12.2) */
|
|
74
|
+
tools: ToolExposure;
|
|
55
75
|
/** mext.config.ts 번들 (.mext/ 기준). 설정 파일이 없으면 생략 */
|
|
56
76
|
config?: string;
|
|
57
77
|
/** instrumentation.ts 번들 (.mext/ 기준). 없으면 생략 */
|
|
58
78
|
instrumentation?: string;
|
|
59
79
|
/** ID 순 */
|
|
60
80
|
commands: BuildManifestCommand[];
|
|
81
|
+
/** MCP 자원 (§12.3). uri 순. 자원이 없으면 생략 */
|
|
82
|
+
resources?: BuildManifestResource[];
|
|
83
|
+
/** MCP 프롬프트 (§12.3). 이름 순. 프롬프트가 없으면 생략 */
|
|
84
|
+
prompts?: BuildManifestPrompt[];
|
|
85
|
+
/** routes/ (§12.4). 경로 순. route 가 없으면 생략. manifestHash 에 포함하지 않는다 (명령어 계약 아님) */
|
|
86
|
+
routes?: BuildManifestRoute[];
|
|
87
|
+
}
|
|
88
|
+
/** .mext/manifest.json 의 route */
|
|
89
|
+
export interface BuildManifestRoute {
|
|
90
|
+
/** URL 경로 패턴 ("/items/[id]") */
|
|
91
|
+
path: string;
|
|
92
|
+
/** routes/ 기준 상대 경로 (posix) */
|
|
93
|
+
file: string;
|
|
94
|
+
/** export 한 메서드. 그 외 = 405 (모듈 import 없음) */
|
|
95
|
+
methods: RouteMethod[];
|
|
96
|
+
/** .mext/ 기준 상대 경로. routes/items/[id].ts → "routes/items/[id].js" */
|
|
97
|
+
module: string;
|
|
61
98
|
}
|
|
62
99
|
/**
|
|
63
100
|
* 명령어 계약의 지문: ID · 별칭 · positional · 입력 스키마 · 안전성 메타데이터 (readOnly · destructive ·
|
|
64
101
|
* requiresConfirmation · scopes) 의 정규화 JSON (키 정렬, ID 순) sha256 hex.
|
|
65
|
-
* 설명 · examples · outputSchema · 파일 경로는 제외 (클라이언트의 help 캐시 · 플래그 파싱에 영향 없음).
|
|
102
|
+
* 설명 · examples · outputSchema · task · 파일 경로는 제외 (클라이언트의 help 캐시 · 플래그 파싱에 영향 없음).
|
|
103
|
+
* extra.resources = 자원 uri (고정 URI · 템플릿 원문, §12.3), extra.prompts = 프롬프트 이름 · args JSON Schema (§12.3).
|
|
104
|
+
* 둘 다 없거나 비면 명령어만의 값과 같다.
|
|
66
105
|
* 빌드 manifest · 서버 (_meta["dev.mext/manifestHash"]) · 클라이언트가 같은 함수로 계산한다
|
|
67
106
|
*/
|
|
68
|
-
export declare function manifestHash(commands: readonly ManifestCommand[]
|
|
107
|
+
export declare function manifestHash(commands: readonly ManifestCommand[], extra?: {
|
|
108
|
+
resources?: readonly string[];
|
|
109
|
+
prompts?: readonly {
|
|
110
|
+
name: string;
|
|
111
|
+
argsSchema?: JsonSchema | undefined;
|
|
112
|
+
}[];
|
|
113
|
+
}): string;
|