@obov/mext 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-cli.d.ts +39 -0
- package/dist/build-cli.js +167 -0
- package/dist/build-cli.js.map +1 -0
- package/dist/build.d.ts +65 -0
- package/dist/build.js +252 -0
- package/dist/build.js.map +1 -0
- package/dist/cli/args.d.ts +107 -0
- package/dist/cli/args.js +621 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +13 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/build.d.ts +11 -0
- package/dist/cli/build.js +87 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev-lock.d.ts +23 -0
- package/dist/cli/dev-lock.js +68 -0
- package/dist/cli/dev-lock.js.map +1 -0
- package/dist/cli/dev-server.d.ts +20 -0
- package/dist/cli/dev-server.js +75 -0
- package/dist/cli/dev-server.js.map +1 -0
- package/dist/cli/dev.d.ts +21 -0
- package/dist/cli/dev.js +565 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/help.d.ts +31 -0
- package/dist/cli/help.js +238 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/io.d.ts +26 -0
- package/dist/cli/io.js +74 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/login.d.ts +32 -0
- package/dist/cli/login.js +284 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/logout.d.ts +9 -0
- package/dist/cli/logout.js +105 -0
- package/dist/cli/logout.js.map +1 -0
- package/dist/cli/main.d.ts +19 -0
- package/dist/cli/main.js +77 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/mcp.d.ts +16 -0
- package/dist/cli/mcp.js +92 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/output.d.ts +78 -0
- package/dist/cli/output.js +229 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/remote.d.ts +17 -0
- package/dist/cli/remote.js +35 -0
- package/dist/cli/remote.js.map +1 -0
- package/dist/cli/run.d.ts +14 -0
- package/dist/cli/run.js +218 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/start.d.ts +8 -0
- package/dist/cli/start.js +37 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/client/cache.d.ts +104 -0
- package/dist/client/cache.js +208 -0
- package/dist/client/cache.js.map +1 -0
- package/dist/client/main.d.ts +31 -0
- package/dist/client/main.js +101 -0
- package/dist/client/main.js.map +1 -0
- package/dist/client/manifest.d.ts +19 -0
- package/dist/client/manifest.js +68 -0
- package/dist/client/manifest.js.map +1 -0
- package/dist/client/runner.d.ts +52 -0
- package/dist/client/runner.js +563 -0
- package/dist/client/runner.js.map +1 -0
- package/dist/command.d.ts +75 -0
- package/dist/command.js +44 -0
- package/dist/command.js.map +1 -0
- package/dist/config.d.ts +111 -0
- package/dist/config.js +197 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.js +12 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.d.ts +97 -0
- package/dist/instrumentation.js +165 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/invoke.d.ts +64 -0
- package/dist/invoke.js +152 -0
- package/dist/invoke.js.map +1 -0
- package/dist/lookup.d.ts +9 -0
- package/dist/lookup.js +27 -0
- package/dist/lookup.js.map +1 -0
- package/dist/manifest.d.ts +68 -0
- package/dist/manifest.js +77 -0
- package/dist/manifest.js.map +1 -0
- package/dist/mcp/constants.d.ts +9 -0
- package/dist/mcp/constants.js +10 -0
- package/dist/mcp/constants.js.map +1 -0
- package/dist/mcp/dispatch.d.ts +66 -0
- package/dist/mcp/dispatch.js +249 -0
- package/dist/mcp/dispatch.js.map +1 -0
- package/dist/mcp/help.d.ts +57 -0
- package/dist/mcp/help.js +136 -0
- package/dist/mcp/help.js.map +1 -0
- package/dist/mcp/http.d.ts +96 -0
- package/dist/mcp/http.js +368 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/install.d.ts +17 -0
- package/dist/mcp/install.js +36 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/mrtr.d.ts +82 -0
- package/dist/mcp/mrtr.js +130 -0
- package/dist/mcp/mrtr.js.map +1 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/server.js +210 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +75 -0
- package/dist/mcp/stdio.js +222 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/principal.d.ts +16 -0
- package/dist/principal.js +24 -0
- package/dist/principal.js.map +1 -0
- package/dist/registry.d.ts +90 -0
- package/dist/registry.js +169 -0
- package/dist/registry.js.map +1 -0
- package/dist/remote/browser.d.ts +15 -0
- package/dist/remote/browser.js +47 -0
- package/dist/remote/browser.js.map +1 -0
- package/dist/remote/client.d.ts +113 -0
- package/dist/remote/client.js +312 -0
- package/dist/remote/client.js.map +1 -0
- package/dist/remote/credentials.d.ts +48 -0
- package/dist/remote/credentials.js +196 -0
- package/dist/remote/credentials.js.map +1 -0
- package/dist/remote/index.d.ts +8 -0
- package/dist/remote/index.js +5 -0
- package/dist/remote/index.js.map +1 -0
- package/dist/remote/oauth.d.ts +186 -0
- package/dist/remote/oauth.js +507 -0
- package/dist/remote/oauth.js.map +1 -0
- package/dist/scan.d.ts +34 -0
- package/dist/scan.js +155 -0
- package/dist/scan.js.map +1 -0
- package/dist/start.d.ts +63 -0
- package/dist/start.js +269 -0
- package/dist/start.js.map +1 -0
- package/dist/suggest.d.ts +7 -0
- package/dist/suggest.js +45 -0
- package/dist/suggest.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -4
- package/README.md +0 -3
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type BuildIssueCode = "COMMANDS_DIR_MISSING" | "PATH_COLLISION" | "DUPLICATE_ID" | "INVALID_SEGMENT" | "INVALID_ALIAS" | "RESERVED_ID" | "ROOT_INDEX" | "INVALID_COMMAND" | "LOAD_FAILED" | "SCHEMA_UNREPRESENTABLE" | "CLI_UNCONVERTIBLE" | "BUNDLE_FAILED" | "INVALID_CONFIG";
|
|
2
|
+
export interface BuildIssue {
|
|
3
|
+
code: BuildIssueCode;
|
|
4
|
+
message: string;
|
|
5
|
+
/** commands/ 기준 상대 경로 (posix) */
|
|
6
|
+
file?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
}
|
|
9
|
+
/** 스캔 · Registry 생성의 빌드 오류. 발견한 문제를 모두 모아 한 번에 던진다 */
|
|
10
|
+
export declare class MextBuildError extends Error {
|
|
11
|
+
readonly issues: readonly BuildIssue[];
|
|
12
|
+
constructor(issues: readonly BuildIssue[]);
|
|
13
|
+
}
|
|
14
|
+
export type MextErrorCode = "UNKNOWN_COMMAND" | "INVALID_ARGUMENT" | "UNAUTHENTICATED" | "PERMISSION_DENIED" | "INPUT_UNAVAILABLE" | "INPUT_DECLINED";
|
|
15
|
+
export interface ArgumentIssue {
|
|
16
|
+
/** 점 구분 경로. "" = 입력 전체 */
|
|
17
|
+
path: string;
|
|
18
|
+
code: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MextErrorDetails {
|
|
22
|
+
UNKNOWN_COMMAND: {
|
|
23
|
+
command: string;
|
|
24
|
+
/** 유사 정식 ID (가까운 순) */
|
|
25
|
+
suggested: string[];
|
|
26
|
+
/** 다음에 볼 help 대상. "help" (루트) · "help users" (네임스페이스) */
|
|
27
|
+
usage: string;
|
|
28
|
+
};
|
|
29
|
+
INVALID_ARGUMENT: {
|
|
30
|
+
command: string;
|
|
31
|
+
/** 원본 입력에 없는 필수 필드 경로 */
|
|
32
|
+
missing_fields: string[];
|
|
33
|
+
issues: ArgumentIssue[];
|
|
34
|
+
/** input JSON Schema 2020-12 */
|
|
35
|
+
expected_schema: Record<string, unknown>;
|
|
36
|
+
examples: unknown[];
|
|
37
|
+
};
|
|
38
|
+
UNAUTHENTICATED: {
|
|
39
|
+
reason: "missing_token" | "invalid_token";
|
|
40
|
+
/** CLI 원격 실행: 토큰 발급 명령 (mext login --url <url>) */
|
|
41
|
+
login?: string;
|
|
42
|
+
};
|
|
43
|
+
PERMISSION_DENIED: {
|
|
44
|
+
command: string;
|
|
45
|
+
/** anonymous = 인증 없는 주체의 readOnly 외 명령어 · read_only = readOnly 전용 호출 (split query) · scope = scopes 부족 */
|
|
46
|
+
reason: "anonymous" | "read_only" | "scope";
|
|
47
|
+
required_scopes: string[];
|
|
48
|
+
held_scopes: string[];
|
|
49
|
+
};
|
|
50
|
+
/** ctx.ask 를 처리할 수 없는 실행 환경 (TTY 없는 CLI 등) */
|
|
51
|
+
INPUT_UNAVAILABLE: {
|
|
52
|
+
command: string;
|
|
53
|
+
};
|
|
54
|
+
/** ctx.ask 거절 · 취소. confirm = requiresConfirmation 확인 거절 (실행 안 함), input = 입력 요청 거절 */
|
|
55
|
+
INPUT_DECLINED: {
|
|
56
|
+
command: string;
|
|
57
|
+
kind: "confirm" | "input";
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** 구조화 오류. 어댑터는 code 로 분기하고 toJSON() 을 응답 본문으로 쓴다 */
|
|
61
|
+
export declare class MextError<C extends MextErrorCode = MextErrorCode> extends Error {
|
|
62
|
+
readonly code: C;
|
|
63
|
+
readonly details: MextErrorDetails[C];
|
|
64
|
+
constructor(code: C, message: string, details: MextErrorDetails[C]);
|
|
65
|
+
toJSON(): {
|
|
66
|
+
code: C;
|
|
67
|
+
message: string;
|
|
68
|
+
} & MextErrorDetails[C];
|
|
69
|
+
}
|
|
70
|
+
export declare function isMextError<C extends MextErrorCode>(error: unknown, code?: C): error is MextError<C>;
|
|
71
|
+
/** auth 단계에서 어댑터가 던지는 오류. anonymous 로 낮추지 않는다 */
|
|
72
|
+
export declare function unauthenticated(reason: MextErrorDetails["UNAUTHENTICATED"]["reason"]): MextError<"UNAUTHENTICATED">;
|
|
73
|
+
/** ctx.ask 거절. AskHandler 가 input 요청 거절 · 취소에 던진다. invoke 는 requiresConfirmation 확인 거절에 던진다 */
|
|
74
|
+
export declare function inputDeclined(command: string, kind: "confirm" | "input"): MextError<"INPUT_DECLINED">;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** 스캔 · Registry 생성의 빌드 오류. 발견한 문제를 모두 모아 한 번에 던진다 */
|
|
2
|
+
export class MextBuildError extends Error {
|
|
3
|
+
issues;
|
|
4
|
+
constructor(issues) {
|
|
5
|
+
super(`mext build failed (${issues.length} issue${issues.length === 1 ? "" : "s"})\n` +
|
|
6
|
+
issues.map((i) => ` ${i.code}${i.file ? ` ${i.file}` : ""}: ${i.message}`).join("\n"));
|
|
7
|
+
this.name = "MextBuildError";
|
|
8
|
+
this.issues = issues;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** 구조화 오류. 어댑터는 code 로 분기하고 toJSON() 을 응답 본문으로 쓴다 */
|
|
12
|
+
export class MextError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
details;
|
|
15
|
+
constructor(code, message, details) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "MextError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.details = details;
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return { code: this.code, message: this.message, ...this.details };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function isMextError(error, code) {
|
|
26
|
+
return error instanceof MextError && (code === undefined || error.code === code);
|
|
27
|
+
}
|
|
28
|
+
/** auth 단계에서 어댑터가 던지는 오류. anonymous 로 낮추지 않는다 */
|
|
29
|
+
export function unauthenticated(reason) {
|
|
30
|
+
const message = reason === "missing_token" ? "Authentication required." : "Invalid authentication token.";
|
|
31
|
+
return new MextError("UNAUTHENTICATED", message, { reason });
|
|
32
|
+
}
|
|
33
|
+
/** ctx.ask 거절. AskHandler 가 input 요청 거절 · 취소에 던진다. invoke 는 requiresConfirmation 확인 거절에 던진다 */
|
|
34
|
+
export function inputDeclined(command, kind) {
|
|
35
|
+
const message = kind === "confirm" ? `${command} was not confirmed and did not run.` : `${command} requested input, but it was declined.`;
|
|
36
|
+
return new MextError("INPUT_DECLINED", message, { command, kind });
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAuBA,sDAAsD;AACtD,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,MAAM,CAAuB;IAEtC,YAAY,MAA6B;QACvC,KAAK,CACH,sBAAsB,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK;YAC7E,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CACzF,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAyDD,qDAAqD;AACrD,MAAM,OAAO,SAAmD,SAAQ,KAAK;IAClE,IAAI,CAAG;IACP,OAAO,CAAqB;IAErC,YAAY,IAAO,EAAE,OAAe,EAAE,OAA4B;QAChE,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,WAAW,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;IACpE,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAA0B,KAAc,EAAE,IAAQ;IAC3E,OAAO,KAAK,YAAY,SAAS,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAClF,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAAC,MAAqD;IACnF,MAAM,OAAO,GAAG,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,+BAA+B,CAAA;IACzG,OAAO,IAAI,SAAS,CAAC,iBAAiB,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;AAC9D,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,IAAyB;IACtE,MAAM,OAAO,GACX,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,qCAAqC,CAAC,CAAC,CAAC,GAAG,OAAO,wCAAwC,CAAA;IAC3H,OAAO,IAAI,SAAS,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;AACpE,CAAC","sourcesContent":["export type BuildIssueCode =\n | \"COMMANDS_DIR_MISSING\"\n | \"PATH_COLLISION\"\n | \"DUPLICATE_ID\"\n | \"INVALID_SEGMENT\"\n | \"INVALID_ALIAS\"\n | \"RESERVED_ID\"\n | \"ROOT_INDEX\"\n | \"INVALID_COMMAND\"\n | \"LOAD_FAILED\"\n | \"SCHEMA_UNREPRESENTABLE\"\n | \"CLI_UNCONVERTIBLE\"\n | \"BUNDLE_FAILED\"\n | \"INVALID_CONFIG\"\n\nexport interface BuildIssue {\n code: BuildIssueCode\n message: string\n /** commands/ 기준 상대 경로 (posix) */\n file?: string\n id?: string\n}\n\n/** 스캔 · Registry 생성의 빌드 오류. 발견한 문제를 모두 모아 한 번에 던진다 */\nexport class MextBuildError extends Error {\n readonly issues: readonly BuildIssue[]\n\n constructor(issues: readonly BuildIssue[]) {\n super(\n `mext build failed (${issues.length} issue${issues.length === 1 ? \"\" : \"s\"})\\n` +\n issues.map((i) => ` ${i.code}${i.file ? ` ${i.file}` : \"\"}: ${i.message}`).join(\"\\n\"),\n )\n this.name = \"MextBuildError\"\n this.issues = issues\n }\n}\n\nexport type MextErrorCode =\n | \"UNKNOWN_COMMAND\"\n | \"INVALID_ARGUMENT\"\n | \"UNAUTHENTICATED\"\n | \"PERMISSION_DENIED\"\n | \"INPUT_UNAVAILABLE\"\n | \"INPUT_DECLINED\"\n\nexport interface ArgumentIssue {\n /** 점 구분 경로. \"\" = 입력 전체 */\n path: string\n code: string\n message: string\n}\n\nexport interface MextErrorDetails {\n UNKNOWN_COMMAND: {\n command: string\n /** 유사 정식 ID (가까운 순) */\n suggested: string[]\n /** 다음에 볼 help 대상. \"help\" (루트) · \"help users\" (네임스페이스) */\n usage: string\n }\n INVALID_ARGUMENT: {\n command: string\n /** 원본 입력에 없는 필수 필드 경로 */\n missing_fields: string[]\n issues: ArgumentIssue[]\n /** input JSON Schema 2020-12 */\n expected_schema: Record<string, unknown>\n examples: unknown[]\n }\n UNAUTHENTICATED: {\n reason: \"missing_token\" | \"invalid_token\"\n /** CLI 원격 실행: 토큰 발급 명령 (mext login --url <url>) */\n login?: string\n }\n PERMISSION_DENIED: {\n command: string\n /** anonymous = 인증 없는 주체의 readOnly 외 명령어 · read_only = readOnly 전용 호출 (split query) · scope = scopes 부족 */\n reason: \"anonymous\" | \"read_only\" | \"scope\"\n required_scopes: string[]\n held_scopes: string[]\n }\n /** ctx.ask 를 처리할 수 없는 실행 환경 (TTY 없는 CLI 등) */\n INPUT_UNAVAILABLE: {\n command: string\n }\n /** ctx.ask 거절 · 취소. confirm = requiresConfirmation 확인 거절 (실행 안 함), input = 입력 요청 거절 */\n INPUT_DECLINED: {\n command: string\n kind: \"confirm\" | \"input\"\n }\n}\n\n/** 구조화 오류. 어댑터는 code 로 분기하고 toJSON() 을 응답 본문으로 쓴다 */\nexport class MextError<C extends MextErrorCode = MextErrorCode> extends Error {\n readonly code: C\n readonly details: MextErrorDetails[C]\n\n constructor(code: C, message: string, details: MextErrorDetails[C]) {\n super(message)\n this.name = \"MextError\"\n this.code = code\n this.details = details\n }\n\n toJSON(): { code: C; message: string } & MextErrorDetails[C] {\n return { code: this.code, message: this.message, ...this.details }\n }\n}\n\nexport function isMextError<C extends MextErrorCode>(error: unknown, code?: C): error is MextError<C> {\n return error instanceof MextError && (code === undefined || error.code === code)\n}\n\n/** auth 단계에서 어댑터가 던지는 오류. anonymous 로 낮추지 않는다 */\nexport function unauthenticated(reason: MextErrorDetails[\"UNAUTHENTICATED\"][\"reason\"]): MextError<\"UNAUTHENTICATED\"> {\n const message = reason === \"missing_token\" ? \"Authentication required.\" : \"Invalid authentication token.\"\n return new MextError(\"UNAUTHENTICATED\", message, { reason })\n}\n\n/** ctx.ask 거절. AskHandler 가 input 요청 거절 · 취소에 던진다. invoke 는 requiresConfirmation 확인 거절에 던진다 */\nexport function inputDeclined(command: string, kind: \"confirm\" | \"input\"): MextError<\"INPUT_DECLINED\"> {\n const message =\n kind === \"confirm\" ? `${command} was not confirmed and did not run.` : `${command} requested input, but it was declined.`\n return new MextError(\"INPUT_DECLINED\", message, { command, kind })\n}\n"]}
|
package/dist/ids.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** 경로 세그먼트 · 별칭 규칙: kebab-case 만 */
|
|
2
|
+
export declare const SEGMENT_PATTERN: RegExp;
|
|
3
|
+
/** 루트에 둘 수 없는 세그먼트 (help 탐색 · CLI help 와 충돌) */
|
|
4
|
+
export declare const RESERVED_ROOT_SEGMENTS: readonly string[];
|
|
5
|
+
export declare function isSegment(value: string): boolean;
|
|
6
|
+
export declare function parentNamespace(id: string): string;
|
package/dist/ids.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 경로 세그먼트 · 별칭 규칙: kebab-case 만 */
|
|
2
|
+
export const SEGMENT_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
3
|
+
/** 루트에 둘 수 없는 세그먼트 (help 탐색 · CLI help 와 충돌) */
|
|
4
|
+
export const RESERVED_ROOT_SEGMENTS = ["help"];
|
|
5
|
+
export function isSegment(value) {
|
|
6
|
+
return SEGMENT_PATTERN.test(value);
|
|
7
|
+
}
|
|
8
|
+
export function parentNamespace(id) {
|
|
9
|
+
const i = id.lastIndexOf(".");
|
|
10
|
+
return i === -1 ? "" : id.slice(0, i);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ids.js.map
|
package/dist/ids.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,CAAC,MAAM,eAAe,GAAG,iCAAiC,CAAA;AAEhE,gDAAgD;AAChD,MAAM,CAAC,MAAM,sBAAsB,GAAsB,CAAC,MAAM,CAAC,CAAA;AAEjE,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACvC,CAAC","sourcesContent":["/** 경로 세그먼트 · 별칭 규칙: kebab-case 만 */\nexport const SEGMENT_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\n/** 루트에 둘 수 없는 세그먼트 (help 탐색 · CLI help 와 충돌) */\nexport const RESERVED_ROOT_SEGMENTS: readonly string[] = [\"help\"]\n\nexport function isSegment(value: string): boolean {\n return SEGMENT_PATTERN.test(value)\n}\n\nexport function parentNamespace(id: string): string {\n const i = id.lastIndexOf(\".\")\n return i === -1 ? \"\" : id.slice(0, i)\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export { VERSION } from "./version.ts";
|
|
2
|
+
export { defineCommand, isCommand } from "./command.ts";
|
|
3
|
+
export type { Ask, AskRequest, Command, CommandContext, CommandDefinition, ConfirmRequest, InputRequest, InputSchema, MextApp, ProgressUpdate, } from "./command.ts";
|
|
4
|
+
export { ANONYMOUS, hasScope, localPrincipal } from "./principal.ts";
|
|
5
|
+
export type { Principal } from "./principal.ts";
|
|
6
|
+
export { inputDeclined, isMextError, MextBuildError, MextError, unauthenticated } from "./errors.ts";
|
|
7
|
+
export type { ArgumentIssue, BuildIssue, BuildIssueCode, MextErrorCode, MextErrorDetails } from "./errors.ts";
|
|
8
|
+
export { authorize, confirmationMessage, createContext, findCommand, invoke, parseInput } from "./invoke.ts";
|
|
9
|
+
export type { AskHandler, AskHandlerRequest, InvokeOptions, InvokeResult } from "./invoke.ts";
|
|
10
|
+
export { findInstrumentation, INSTRUMENTATION_FILE, loadInstrumentation, loadProjectInstrumentation, } from "./instrumentation.ts";
|
|
11
|
+
export type { CommandErrorInfo, Instrumentation, InstrumentationModule, LoadInstrumentationOptions, OtelApi, Transport, } from "./instrumentation.ts";
|
|
12
|
+
export { collectRegistry, createLazyRegistry, createRegistry, loadCommand, resolveCommand } from "./registry.ts";
|
|
13
|
+
export type { JsonSchema, LazyCommandInput, NamespaceInfo, RegisteredCommand, Registry, RegistryCommandInput, RegistryInput, RegistryNamespaceInput, } from "./registry.ts";
|
|
14
|
+
export { collectCommands, loadRegistry, scanCommands, walkCommands } from "./scan.ts";
|
|
15
|
+
export type { ScannedFile, ScanResult } from "./scan.ts";
|
|
16
|
+
export { buildManifest, describeCommand, generateManifest, MANIFEST_SCHEMA_VERSION, manifestHash } from "./manifest.ts";
|
|
17
|
+
export type { BuildManifest, BuildManifestCommand, Manifest, ManifestCommand, ManifestNamespace } from "./manifest.ts";
|
|
18
|
+
export { BUILD_DIR, BUILD_MANIFEST_FILE, buildProject, HANDLER_JS, RolldownUnavailableError, SERVER_JS, START_ENTRY } from "./build.ts";
|
|
19
|
+
export type { BuildOptions, BuildResult, BuildWarning, RolldownModule, RolldownOutputItem } from "./build.ts";
|
|
20
|
+
export { formatBuildSummary, runBuild } from "./cli/build.ts";
|
|
21
|
+
export { mextVersion } from "./version.ts";
|
|
22
|
+
export * from "./mcp/index.ts";
|
|
23
|
+
export { CLI_BUILTIN_COMMANDS, CONFIG_FILE, createApp, defineConfig, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from "./config.ts";
|
|
24
|
+
export type { CliConfig, McpCapabilities, McpConfig, McpConnectHandler, McpConnection, MextConfig } from "./config.ts";
|
|
25
|
+
export { buildCustomerCli, CLI_BIN_FILE, CLI_DIR, customerCliIssues } from "./build-cli.ts";
|
|
26
|
+
export type { BuildCustomerCliOptions, CustomerCliBuild } from "./build-cli.ts";
|
|
27
|
+
export { runCustomerCli } from "./client/main.ts";
|
|
28
|
+
export type { CustomerCliConfig } from "./client/main.ts";
|
|
29
|
+
export { clientManifest, manifestHelp } from "./client/manifest.ts";
|
|
30
|
+
export type { ClientManifest, ClientManifestCommand } from "./client/manifest.ts";
|
|
31
|
+
export { runRemote } from "./client/runner.ts";
|
|
32
|
+
export type { HelpSource, RemoteRunOptions } from "./client/runner.ts";
|
|
33
|
+
export { runCli, SUBCOMMANDS } from "./cli/main.ts";
|
|
34
|
+
export type { CliSubcommand } from "./cli/main.ts";
|
|
35
|
+
export type { CliIo, CliWriter } from "./cli/io.ts";
|
|
36
|
+
export { analyzeInput, assertCliConvertible, cliBuildIssues, cliSpec, parseArgs, RESERVED_FLAGS, toKebab } from "./cli/args.ts";
|
|
37
|
+
export type { CliField, CliSpec, FieldKind, ParseArgsOptions, ParsedArgs, Primitive, ScalarKind } from "./cli/args.ts";
|
|
38
|
+
export { cliPath, formatInvocation, renderCommandHelp, renderHelp, renderNamespaceHelp, resolveHelpTarget, } from "./cli/help.ts";
|
|
39
|
+
export type { HelpOptions, HelpTarget } from "./cli/help.ts";
|
|
40
|
+
export { errorJson, exitCodeOf, formatError, formatJson, formatResult } from "./cli/output.ts";
|
|
41
|
+
export type { CliErrorFormatter, CliErrorInfo, CliErrorOutput } from "./cli/output.ts";
|
|
42
|
+
export { loginCommand, resolveServerUrl } from "./cli/login.ts";
|
|
43
|
+
export type { AuthCommandOptions } from "./cli/login.ts";
|
|
44
|
+
export { logoutCommand } from "./cli/logout.ts";
|
|
45
|
+
export * from "./remote/index.ts";
|
|
46
|
+
export { cliSpecFromJsonSchema, defineCliSpec } from "./cli/args.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { VERSION } from "./version.js";
|
|
2
|
+
export { defineCommand, isCommand } from "./command.js";
|
|
3
|
+
export { ANONYMOUS, hasScope, localPrincipal } from "./principal.js";
|
|
4
|
+
export { inputDeclined, isMextError, MextBuildError, MextError, unauthenticated } from "./errors.js";
|
|
5
|
+
export { authorize, confirmationMessage, createContext, findCommand, invoke, parseInput } from "./invoke.js";
|
|
6
|
+
export { findInstrumentation, INSTRUMENTATION_FILE, loadInstrumentation, loadProjectInstrumentation, } from "./instrumentation.js";
|
|
7
|
+
export { collectRegistry, createLazyRegistry, createRegistry, loadCommand, resolveCommand } from "./registry.js";
|
|
8
|
+
export { collectCommands, loadRegistry, scanCommands, walkCommands } from "./scan.js";
|
|
9
|
+
export { buildManifest, describeCommand, generateManifest, MANIFEST_SCHEMA_VERSION, manifestHash } from "./manifest.js";
|
|
10
|
+
export { BUILD_DIR, BUILD_MANIFEST_FILE, buildProject, HANDLER_JS, RolldownUnavailableError, SERVER_JS, START_ENTRY } from "./build.js";
|
|
11
|
+
export { formatBuildSummary, runBuild } from "./cli/build.js";
|
|
12
|
+
export { mextVersion } from "./version.js";
|
|
13
|
+
export * from "./mcp/index.js";
|
|
14
|
+
export { CLI_BUILTIN_COMMANDS, CONFIG_FILE, createApp, defineConfig, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from "./config.js";
|
|
15
|
+
export { buildCustomerCli, CLI_BIN_FILE, CLI_DIR, customerCliIssues } from "./build-cli.js";
|
|
16
|
+
export { runCustomerCli } from "./client/main.js";
|
|
17
|
+
export { clientManifest, manifestHelp } from "./client/manifest.js";
|
|
18
|
+
export { runRemote } from "./client/runner.js";
|
|
19
|
+
export { runCli, SUBCOMMANDS } from "./cli/main.js";
|
|
20
|
+
export { analyzeInput, assertCliConvertible, cliBuildIssues, cliSpec, parseArgs, RESERVED_FLAGS, toKebab } from "./cli/args.js";
|
|
21
|
+
export { cliPath, formatInvocation, renderCommandHelp, renderHelp, renderNamespaceHelp, resolveHelpTarget, } from "./cli/help.js";
|
|
22
|
+
export { errorJson, exitCodeOf, formatError, formatJson, formatResult } from "./cli/output.js";
|
|
23
|
+
export { loginCommand, resolveServerUrl } from "./cli/login.js";
|
|
24
|
+
export { logoutCommand } from "./cli/logout.js";
|
|
25
|
+
export * from "./remote/index.js";
|
|
26
|
+
export { cliSpecFromJsonSchema, defineCliSpec } from "./cli/args.js";
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAavD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEpG,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5G,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAA;AAS7B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAWhH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAErF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEvH,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEvI,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE/I,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAE3F,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE/H,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9F,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAE/D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,cAAc,mBAAmB,CAAA;AACjC,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export { VERSION } from \"./version.ts\"\n\nexport { defineCommand, isCommand } from \"./command.ts\"\nexport type {\n Ask,\n AskRequest,\n Command,\n CommandContext,\n CommandDefinition,\n ConfirmRequest,\n InputRequest,\n InputSchema,\n MextApp,\n ProgressUpdate,\n} from \"./command.ts\"\nexport { ANONYMOUS, hasScope, localPrincipal } from \"./principal.ts\"\nexport type { Principal } from \"./principal.ts\"\nexport { inputDeclined, isMextError, MextBuildError, MextError, unauthenticated } from \"./errors.ts\"\nexport type { ArgumentIssue, BuildIssue, BuildIssueCode, MextErrorCode, MextErrorDetails } from \"./errors.ts\"\nexport { authorize, confirmationMessage, createContext, findCommand, invoke, parseInput } from \"./invoke.ts\"\nexport type { AskHandler, AskHandlerRequest, InvokeOptions, InvokeResult } from \"./invoke.ts\"\nexport {\n findInstrumentation,\n INSTRUMENTATION_FILE,\n loadInstrumentation,\n loadProjectInstrumentation,\n} from \"./instrumentation.ts\"\nexport type {\n CommandErrorInfo,\n Instrumentation,\n InstrumentationModule,\n LoadInstrumentationOptions,\n OtelApi,\n Transport,\n} from \"./instrumentation.ts\"\nexport { collectRegistry, createLazyRegistry, createRegistry, loadCommand, resolveCommand } from \"./registry.ts\"\nexport type {\n JsonSchema,\n LazyCommandInput,\n NamespaceInfo,\n RegisteredCommand,\n Registry,\n RegistryCommandInput,\n RegistryInput,\n RegistryNamespaceInput,\n} from \"./registry.ts\"\nexport { collectCommands, loadRegistry, scanCommands, walkCommands } from \"./scan.ts\"\nexport type { ScannedFile, ScanResult } from \"./scan.ts\"\nexport { buildManifest, describeCommand, generateManifest, MANIFEST_SCHEMA_VERSION, manifestHash } from \"./manifest.ts\"\nexport type { BuildManifest, BuildManifestCommand, Manifest, ManifestCommand, ManifestNamespace } from \"./manifest.ts\"\nexport { BUILD_DIR, BUILD_MANIFEST_FILE, buildProject, HANDLER_JS, RolldownUnavailableError, SERVER_JS, START_ENTRY } from \"./build.ts\"\nexport type { BuildOptions, BuildResult, BuildWarning, RolldownModule, RolldownOutputItem } from \"./build.ts\"\nexport { formatBuildSummary, runBuild } from \"./cli/build.ts\"\nexport { mextVersion } from \"./version.ts\"\nexport * from \"./mcp/index.ts\"\nexport { CLI_BUILTIN_COMMANDS, CONFIG_FILE, createApp, defineConfig, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from \"./config.ts\"\nexport type { CliConfig, McpCapabilities, McpConfig, McpConnectHandler, McpConnection, MextConfig } from \"./config.ts\"\nexport { buildCustomerCli, CLI_BIN_FILE, CLI_DIR, customerCliIssues } from \"./build-cli.ts\"\nexport type { BuildCustomerCliOptions, CustomerCliBuild } from \"./build-cli.ts\"\nexport { runCustomerCli } from \"./client/main.ts\"\nexport type { CustomerCliConfig } from \"./client/main.ts\"\nexport { clientManifest, manifestHelp } from \"./client/manifest.ts\"\nexport type { ClientManifest, ClientManifestCommand } from \"./client/manifest.ts\"\nexport { runRemote } from \"./client/runner.ts\"\nexport type { HelpSource, RemoteRunOptions } from \"./client/runner.ts\"\nexport { runCli, SUBCOMMANDS } from \"./cli/main.ts\"\nexport type { CliSubcommand } from \"./cli/main.ts\"\nexport type { CliIo, CliWriter } from \"./cli/io.ts\"\nexport { analyzeInput, assertCliConvertible, cliBuildIssues, cliSpec, parseArgs, RESERVED_FLAGS, toKebab } from \"./cli/args.ts\"\nexport type { CliField, CliSpec, FieldKind, ParseArgsOptions, ParsedArgs, Primitive, ScalarKind } from \"./cli/args.ts\"\nexport {\n cliPath,\n formatInvocation,\n renderCommandHelp,\n renderHelp,\n renderNamespaceHelp,\n resolveHelpTarget,\n} from \"./cli/help.ts\"\nexport type { HelpOptions, HelpTarget } from \"./cli/help.ts\"\nexport { errorJson, exitCodeOf, formatError, formatJson, formatResult } from \"./cli/output.ts\"\nexport type { CliErrorFormatter, CliErrorInfo, CliErrorOutput } from \"./cli/output.ts\"\nexport { loginCommand, resolveServerUrl } from \"./cli/login.ts\"\nexport type { AuthCommandOptions } from \"./cli/login.ts\"\nexport { logoutCommand } from \"./cli/logout.ts\"\nexport * from \"./remote/index.ts\"\nexport { cliSpecFromJsonSchema, defineCliSpec } from \"./cli/args.ts\"\n"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Principal } from "./principal.ts";
|
|
2
|
+
/**
|
|
3
|
+
* instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).
|
|
4
|
+
* register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.
|
|
5
|
+
* span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).
|
|
6
|
+
*/
|
|
7
|
+
export declare const INSTRUMENTATION_FILE = "instrumentation.ts";
|
|
8
|
+
/** 핸들러를 실행한 어댑터. 원격 CLI 는 핸들러를 실행하지 않으므로 없다 */
|
|
9
|
+
export type Transport = "http" | "stdio" | "local";
|
|
10
|
+
export interface CommandErrorInfo {
|
|
11
|
+
/** 정식 명령어 ID */
|
|
12
|
+
command: string;
|
|
13
|
+
transport: Transport;
|
|
14
|
+
/** scopes · claims 는 넘기지 않는다 */
|
|
15
|
+
principal: {
|
|
16
|
+
id: string;
|
|
17
|
+
kind: Principal["kind"];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** instrumentation.ts 의 export */
|
|
21
|
+
export interface InstrumentationModule {
|
|
22
|
+
register?: () => unknown;
|
|
23
|
+
onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown;
|
|
24
|
+
}
|
|
25
|
+
/** 어댑터가 InvokeOptions.instrumentation 으로 넘기는 로드 결과 */
|
|
26
|
+
export interface Instrumentation {
|
|
27
|
+
/** 핸들러 예외 (MextError 외, 취소 · MRTR 대기 제외). 자체 예외는 stderr 로 보고하고 응답은 바꾸지 않는다 */
|
|
28
|
+
onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown;
|
|
29
|
+
/** @opentelemetry/api. undefined = span 없음 */
|
|
30
|
+
otel?: OtelApi;
|
|
31
|
+
/** onCommandError 의 예외 보고. 기본 stderr */
|
|
32
|
+
onerror?: (error: unknown) => void;
|
|
33
|
+
}
|
|
34
|
+
export interface LoadInstrumentationOptions {
|
|
35
|
+
/** @opentelemetry/api 로더 (테스트 주입). 기본 = 동적 import, 결과는 프로세스당 캐시 */
|
|
36
|
+
importOtel?: () => Promise<unknown>;
|
|
37
|
+
onerror?: (error: unknown) => void;
|
|
38
|
+
}
|
|
39
|
+
/** mext 가 쓰는 @opentelemetry/api 의 부분 (타입 의존 없이) */
|
|
40
|
+
export interface OtelApi {
|
|
41
|
+
trace: {
|
|
42
|
+
getTracer(name: string, version?: string): OtelTracer;
|
|
43
|
+
};
|
|
44
|
+
context: {
|
|
45
|
+
active(): unknown;
|
|
46
|
+
};
|
|
47
|
+
propagation: {
|
|
48
|
+
extract(context: unknown, carrier: unknown): unknown;
|
|
49
|
+
};
|
|
50
|
+
SpanStatusCode: {
|
|
51
|
+
ERROR: number;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
interface OtelTracer {
|
|
55
|
+
startActiveSpan<T>(name: string, options: {
|
|
56
|
+
attributes: Record<string, string>;
|
|
57
|
+
}, context: unknown, fn: (span: OtelSpan) => T): T;
|
|
58
|
+
}
|
|
59
|
+
interface OtelSpan {
|
|
60
|
+
setAttribute(key: string, value: string): unknown;
|
|
61
|
+
setStatus(status: {
|
|
62
|
+
code: number;
|
|
63
|
+
message?: string;
|
|
64
|
+
}): unknown;
|
|
65
|
+
recordException(exception: unknown): unknown;
|
|
66
|
+
end(): void;
|
|
67
|
+
}
|
|
68
|
+
/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */
|
|
69
|
+
export declare function findInstrumentation(cwd?: string): string | undefined;
|
|
70
|
+
/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */
|
|
71
|
+
export declare function loadProjectInstrumentation(cwd?: string, options?: LoadInstrumentationOptions): Promise<Instrumentation>;
|
|
72
|
+
/**
|
|
73
|
+
* instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.
|
|
74
|
+
* 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).
|
|
75
|
+
* source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.
|
|
76
|
+
*/
|
|
77
|
+
export declare function loadInstrumentation(source?: string | URL | InstrumentationModule, options?: LoadInstrumentationOptions): Promise<Instrumentation>;
|
|
78
|
+
export interface ObserveOptions {
|
|
79
|
+
instrumentation?: Instrumentation;
|
|
80
|
+
transport?: Transport;
|
|
81
|
+
principal: Principal;
|
|
82
|
+
/** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate) */
|
|
83
|
+
traceContext?: Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).
|
|
87
|
+
* 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.
|
|
88
|
+
* 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError 메시지는 입력 일부를 담을 수 있어 제외).
|
|
89
|
+
* 취소 (signal abort · MRTR 대기) = status 없음.
|
|
90
|
+
*/
|
|
91
|
+
export declare function observeCommand<T>(command: string, options: ObserveOptions, signal: () => AbortSignal | undefined, fn: () => Promise<T>): Promise<T>;
|
|
92
|
+
/**
|
|
93
|
+
* 핸들러 예외 → onCommandError (await). MextError · 취소 · MRTR 대기는 보고하지 않는다.
|
|
94
|
+
* onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.
|
|
95
|
+
*/
|
|
96
|
+
export declare function reportCommandError(error: unknown, command: string, options: ObserveOptions, signal: AbortSignal | undefined): Promise<void>;
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
11
|
+
import { pathToFileURL } from "node:url";
|
|
12
|
+
import { isMextError } from "./errors.js";
|
|
13
|
+
/**
|
|
14
|
+
* instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).
|
|
15
|
+
* register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.
|
|
16
|
+
* span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).
|
|
17
|
+
*/
|
|
18
|
+
export const INSTRUMENTATION_FILE = "instrumentation.ts";
|
|
19
|
+
/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */
|
|
20
|
+
export function findInstrumentation(cwd = process.cwd()) {
|
|
21
|
+
const file = join(resolve(cwd), INSTRUMENTATION_FILE);
|
|
22
|
+
return existsSync(file) ? file : undefined;
|
|
23
|
+
}
|
|
24
|
+
/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */
|
|
25
|
+
export function loadProjectInstrumentation(cwd = process.cwd(), options = {}) {
|
|
26
|
+
return loadInstrumentation(findInstrumentation(cwd), options);
|
|
27
|
+
}
|
|
28
|
+
const loaded = new Map();
|
|
29
|
+
const loadedModules = new WeakMap();
|
|
30
|
+
/**
|
|
31
|
+
* instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.
|
|
32
|
+
* 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).
|
|
33
|
+
* source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.
|
|
34
|
+
*/
|
|
35
|
+
export async function loadInstrumentation(source, options = {}) {
|
|
36
|
+
const [mod, otel] = await Promise.all([
|
|
37
|
+
source === undefined ? Promise.resolve({}) : registerOnce(source),
|
|
38
|
+
options.importOtel ? resolveOtel(options.importOtel) : defaultOtel(),
|
|
39
|
+
]);
|
|
40
|
+
return {
|
|
41
|
+
...(mod.onCommandError ? { onCommandError: mod.onCommandError } : {}),
|
|
42
|
+
...(otel ? { otel } : {}),
|
|
43
|
+
...(options.onerror ? { onerror: options.onerror } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function registerOnce(source) {
|
|
47
|
+
if (typeof source === "string" || source instanceof URL) {
|
|
48
|
+
const isPath = typeof source === "string" && (isAbsolute(source) || !/^[a-z][a-z0-9+.-]+:/i.test(source));
|
|
49
|
+
const href = isPath ? pathToFileURL(resolve(source)).href : String(source);
|
|
50
|
+
return memo(loaded, href, () => import(__rewriteRelativeImportExtension(href)).then((m) => register(m, href)));
|
|
51
|
+
}
|
|
52
|
+
return memo(loadedModules, source, () => register(source, "instrumentation"));
|
|
53
|
+
}
|
|
54
|
+
function memo(cache, key, load) {
|
|
55
|
+
let pending = cache.get(key);
|
|
56
|
+
if (!pending) {
|
|
57
|
+
pending = load().catch((error) => {
|
|
58
|
+
cache.delete(key);
|
|
59
|
+
throw error;
|
|
60
|
+
});
|
|
61
|
+
cache.set(key, pending);
|
|
62
|
+
}
|
|
63
|
+
return pending;
|
|
64
|
+
}
|
|
65
|
+
async function register(value, label) {
|
|
66
|
+
const mod = (value ?? {});
|
|
67
|
+
for (const key of ["register", "onCommandError"]) {
|
|
68
|
+
if (mod[key] !== undefined && typeof mod[key] !== "function") {
|
|
69
|
+
throw new TypeError(`${label}: export ${key} must be a function`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const hooks = mod;
|
|
73
|
+
await hooks.register?.();
|
|
74
|
+
return hooks;
|
|
75
|
+
}
|
|
76
|
+
/* @opentelemetry/api (선택 peer). 번들러가 해석 · 포함하지 않도록 specifier 는 변수 (R5) */
|
|
77
|
+
const OTEL_API = "@opentelemetry/api";
|
|
78
|
+
let otelDefault;
|
|
79
|
+
function defaultOtel() {
|
|
80
|
+
otelDefault ??= resolveOtel(() => import(__rewriteRelativeImportExtension(OTEL_API)));
|
|
81
|
+
return otelDefault;
|
|
82
|
+
}
|
|
83
|
+
async function resolveOtel(importer) {
|
|
84
|
+
try {
|
|
85
|
+
const mod = (await importer());
|
|
86
|
+
const api = mod.trace ? mod : mod.default;
|
|
87
|
+
return api?.trace && api.context && api.propagation && api.SpanStatusCode ? api : undefined;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/* invoke 에서 사용 */
|
|
94
|
+
const TRACER_NAME = "@obov/mext";
|
|
95
|
+
/**
|
|
96
|
+
* 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).
|
|
97
|
+
* 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.
|
|
98
|
+
* 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError 메시지는 입력 일부를 담을 수 있어 제외).
|
|
99
|
+
* 취소 (signal abort · MRTR 대기) = status 없음.
|
|
100
|
+
*/
|
|
101
|
+
export function observeCommand(command, options, signal, fn) {
|
|
102
|
+
const otel = options.instrumentation?.otel;
|
|
103
|
+
if (!otel)
|
|
104
|
+
return fn();
|
|
105
|
+
const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active();
|
|
106
|
+
const attributes = {
|
|
107
|
+
"mext.command.id": command,
|
|
108
|
+
"mext.transport": options.transport ?? "local",
|
|
109
|
+
"mext.principal.kind": options.principal.kind,
|
|
110
|
+
};
|
|
111
|
+
const tracer = otel.trace.getTracer(TRACER_NAME);
|
|
112
|
+
return tracer.startActiveSpan(`mext.command ${command}`, { attributes }, parent, async (span) => {
|
|
113
|
+
try {
|
|
114
|
+
return await fn();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (!signal()?.aborted) {
|
|
118
|
+
if (isMextError(error)) {
|
|
119
|
+
span.setAttribute("mext.error.code", error.code);
|
|
120
|
+
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error.code });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
span.setAttribute("mext.error.code", "EXECUTION_ERROR");
|
|
124
|
+
span.recordException(error);
|
|
125
|
+
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
span.end();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 핸들러 예외 → onCommandError (await). MextError · 취소 · MRTR 대기는 보고하지 않는다.
|
|
137
|
+
* onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.
|
|
138
|
+
*/
|
|
139
|
+
export async function reportCommandError(error, command, options, signal) {
|
|
140
|
+
const hook = options.instrumentation?.onCommandError;
|
|
141
|
+
if (!hook || isMextError(error) || signal?.aborted)
|
|
142
|
+
return;
|
|
143
|
+
const info = {
|
|
144
|
+
command,
|
|
145
|
+
transport: options.transport ?? "local",
|
|
146
|
+
principal: { id: options.principal.id, kind: options.principal.kind },
|
|
147
|
+
};
|
|
148
|
+
try {
|
|
149
|
+
await hook(error, info);
|
|
150
|
+
}
|
|
151
|
+
catch (hookError) {
|
|
152
|
+
const onerror = options.instrumentation?.onerror ?? defaultOnError;
|
|
153
|
+
try {
|
|
154
|
+
onerror(hookError);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// 보고는 응답을 바꾸지 않는다
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function defaultOnError(error) {
|
|
162
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
163
|
+
process.stderr.write(`mext: onCommandError threw: ${message}\n`);
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +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 { isMextError } 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 메시지는 입력 일부를 담을 수 있어 제외).\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 (isMextError(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 · 취소 · 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 || isMextError(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"]}
|
package/dist/invoke.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import type { CommandContext, InputSchema, MextApp, ProgressUpdate } from "./command.ts";
|
|
3
|
+
import { type Instrumentation, type Transport } from "./instrumentation.ts";
|
|
4
|
+
import { findCommand } from "./lookup.ts";
|
|
5
|
+
import { type Principal } from "./principal.ts";
|
|
6
|
+
import { type JsonSchema, type RegisteredCommand, type Registry } from "./registry.ts";
|
|
7
|
+
/** 어댑터가 주입하는 ctx.ask 구현. command = 정식 ID */
|
|
8
|
+
export type AskHandler = (request: AskHandlerRequest) => Promise<unknown>;
|
|
9
|
+
export type AskHandlerRequest = {
|
|
10
|
+
command: string;
|
|
11
|
+
kind: "confirm";
|
|
12
|
+
message: string;
|
|
13
|
+
} | {
|
|
14
|
+
command: string;
|
|
15
|
+
kind: "input";
|
|
16
|
+
message: string;
|
|
17
|
+
schema: InputSchema;
|
|
18
|
+
requestedSchema: JsonSchema;
|
|
19
|
+
};
|
|
20
|
+
export interface InvokeOptions {
|
|
21
|
+
principal: Principal;
|
|
22
|
+
/** 기본 = abort 되지 않는 signal */
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
/** 기본 = 무시 */
|
|
25
|
+
progress?: (update: ProgressUpdate) => void;
|
|
26
|
+
/** 기본 = ctx.ask 호출 시 INPUT_UNAVAILABLE */
|
|
27
|
+
ask?: AskHandler;
|
|
28
|
+
app?: MextApp;
|
|
29
|
+
/** readOnly 명령어만 허용 (split 의 query 도구). 그 외 = PERMISSION_DENIED */
|
|
30
|
+
readOnly?: boolean;
|
|
31
|
+
/** loadInstrumentation 결과. onCommandError · mext.command span. 기본 = 없음 */
|
|
32
|
+
instrumentation?: Instrumentation;
|
|
33
|
+
/** 실행 어댑터 (onCommandError · span 속성). 기본 "local" */
|
|
34
|
+
transport?: Transport;
|
|
35
|
+
/** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate). span 의 부모 */
|
|
36
|
+
traceContext?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
export interface InvokeResult {
|
|
39
|
+
/** 정식 ID (별칭으로 호출해도) */
|
|
40
|
+
command: string;
|
|
41
|
+
value: unknown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 어댑터 공통 실행기: ID 해석 (별칭 포함) → 권한 → (lazy 면 모듈 import) → Zod safeParse 1회 → 확인 (requiresConfirmation) → execute.
|
|
45
|
+
* 실패는 MextError (UNKNOWN_COMMAND · PERMISSION_DENIED · INVALID_ARGUMENT · INPUT_UNAVAILABLE · INPUT_DECLINED).
|
|
46
|
+
* 핸들러 예외는 그대로 전파 (instrumentation 있으면 onCommandError 보고 후).
|
|
47
|
+
* instrumentation.otel 있으면 findCommand 뒤 전체를 `mext.command <id>` span 으로 감싼다.
|
|
48
|
+
*/
|
|
49
|
+
export declare function invoke(registry: Registry, id: string, rawInput: unknown, options: InvokeOptions): Promise<InvokeResult>;
|
|
50
|
+
/** ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage). 구현 = lookup.ts (help · 고객 CLI 공용) */
|
|
51
|
+
export { findCommand };
|
|
52
|
+
/**
|
|
53
|
+
* 안전성 메타데이터를 주체에 대해 강제.
|
|
54
|
+
* readOnly 전용 호출 = readOnly 명령어만 · anonymous = readOnly 명령어만 · command.scopes 전부 보유 ("*" = 전체).
|
|
55
|
+
*/
|
|
56
|
+
export declare function authorize(entry: RegisteredCommand, principal: Principal, options?: {
|
|
57
|
+
readOnly?: boolean;
|
|
58
|
+
}): void;
|
|
59
|
+
/** Zod safeParse 1회. 실패 = INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples) */
|
|
60
|
+
export declare function parseInput(entry: RegisteredCommand, rawInput: unknown): Promise<z.output<InputSchema>>;
|
|
61
|
+
/** ctx 생성. ask · progress · signal 은 어댑터 주입 */
|
|
62
|
+
export declare function createContext(entry: RegisteredCommand, options: InvokeOptions): CommandContext;
|
|
63
|
+
/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */
|
|
64
|
+
export declare function confirmationMessage(entry: RegisteredCommand, input: unknown): string;
|