@obov/mext 0.5.0 → 0.7.1
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/approval.d.ts +33 -0
- package/dist/approval.js +36 -0
- package/dist/approval.js.map +1 -0
- package/dist/audit.d.ts +123 -0
- package/dist/audit.js +221 -0
- package/dist/audit.js.map +1 -0
- package/dist/build-cli.d.ts +1 -0
- package/dist/build-cli.js.map +1 -1
- package/dist/build.d.ts +2 -0
- package/dist/build.js +46 -5
- package/dist/build.js.map +1 -1
- package/dist/caller.d.ts +53 -0
- package/dist/caller.js +51 -0
- package/dist/caller.js.map +1 -0
- package/dist/cli/args.js +1 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/build.js +26 -4
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/dev-audit.d.ts +25 -0
- package/dist/cli/dev-audit.js +84 -0
- package/dist/cli/dev-audit.js.map +1 -0
- package/dist/cli/dev-lock.d.ts +3 -1
- package/dist/cli/dev-lock.js +12 -5
- package/dist/cli/dev-lock.js.map +1 -1
- package/dist/cli/dev-server.d.ts +9 -0
- package/dist/cli/dev-server.js +27 -3
- package/dist/cli/dev-server.js.map +1 -1
- package/dist/cli/dev-tap.d.ts +20 -0
- package/dist/cli/dev-tap.js +70 -0
- package/dist/cli/dev-tap.js.map +1 -0
- package/dist/cli/dev.d.ts +6 -0
- package/dist/cli/dev.js +20 -8
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/help.js +2 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/inspect.d.ts +7 -0
- package/dist/cli/inspect.js +699 -0
- package/dist/cli/inspect.js.map +1 -0
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/media.d.ts +19 -0
- package/dist/cli/media.js +77 -0
- package/dist/cli/media.js.map +1 -0
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +3 -2
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/run.js +33 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/serve.d.ts +10 -0
- package/dist/cli/serve.js +114 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/types.d.ts +9 -0
- package/dist/cli/types.js +77 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/manifest.js +6 -2
- package/dist/client/manifest.js.map +1 -1
- package/dist/client/runner.js +40 -3
- package/dist/client/runner.js.map +1 -1
- package/dist/client/typed.d.ts +56 -0
- package/dist/client/typed.js +526 -0
- package/dist/client/typed.js.map +1 -0
- package/dist/client/types.d.ts +225 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/command.d.ts +61 -5
- package/dist/command.js +42 -1
- package/dist/command.js.map +1 -1
- package/dist/config.d.ts +59 -10
- package/dist/config.js +67 -7
- package/dist/config.js.map +1 -1
- package/dist/content.d.ts +65 -0
- package/dist/content.js +99 -0
- package/dist/content.js.map +1 -0
- package/dist/errors.d.ts +21 -3
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/expose.d.ts +9 -0
- package/dist/expose.js +52 -0
- package/dist/expose.js.map +1 -0
- package/dist/index.d.ts +18 -7
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect/agent.d.ts +104 -0
- package/dist/inspect/agent.js +125 -0
- package/dist/inspect/agent.js.map +1 -0
- package/dist/inspect/audit.d.ts +46 -0
- package/dist/inspect/audit.js +133 -0
- package/dist/inspect/audit.js.map +1 -0
- package/dist/inspect/compare.d.ts +12 -0
- package/dist/inspect/compare.js +31 -0
- package/dist/inspect/compare.js.map +1 -0
- package/dist/inspect/index.d.ts +17 -0
- package/dist/inspect/index.js +12 -0
- package/dist/inspect/index.js.map +1 -0
- package/dist/inspect/inspector.d.ts +168 -0
- package/dist/inspect/inspector.js +392 -0
- package/dist/inspect/inspector.js.map +1 -0
- package/dist/inspect/profiles.d.ts +95 -0
- package/dist/inspect/profiles.js +235 -0
- package/dist/inspect/profiles.js.map +1 -0
- package/dist/inspect/scenario.d.ts +126 -0
- package/dist/inspect/scenario.js +555 -0
- package/dist/inspect/scenario.js.map +1 -0
- package/dist/inspect/ui-server.d.ts +31 -0
- package/dist/inspect/ui-server.js +378 -0
- package/dist/inspect/ui-server.js.map +1 -0
- package/dist/inspect/ui.html +917 -0
- package/dist/instrumentation.d.ts +13 -2
- package/dist/instrumentation.js +11 -1
- package/dist/instrumentation.js.map +1 -1
- package/dist/invoke.d.ts +36 -5
- package/dist/invoke.js +162 -21
- package/dist/invoke.js.map +1 -1
- package/dist/manifest.d.ts +19 -3
- package/dist/manifest.js +5 -1
- package/dist/manifest.js.map +1 -1
- package/dist/mcp/constants.d.ts +7 -0
- package/dist/mcp/constants.js +7 -0
- package/dist/mcp/constants.js.map +1 -1
- package/dist/mcp/dispatch.d.ts +10 -4
- package/dist/mcp/dispatch.js +24 -7
- package/dist/mcp/dispatch.js.map +1 -1
- package/dist/mcp/help.d.ts +2 -0
- package/dist/mcp/help.js +6 -1
- package/dist/mcp/help.js.map +1 -1
- package/dist/mcp/host.d.ts +14 -0
- package/dist/mcp/host.js +48 -0
- package/dist/mcp/host.js.map +1 -0
- package/dist/mcp/http.d.ts +38 -7
- package/dist/mcp/http.js +199 -23
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.js +2 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +12 -1
- package/dist/mcp/server.js +63 -17
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts +4 -0
- package/dist/mcp/stdio.js +3 -0
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp/tasks.d.ts +3 -1
- package/dist/mcp/tasks.js +7 -4
- package/dist/mcp/tasks.js.map +1 -1
- package/dist/principal.d.ts +22 -0
- package/dist/principal.js +29 -0
- package/dist/principal.js.map +1 -1
- package/dist/registry.d.ts +5 -0
- package/dist/registry.js +21 -0
- package/dist/registry.js.map +1 -1
- package/dist/remote/client.d.ts +26 -1
- package/dist/remote/client.js +180 -55
- package/dist/remote/client.js.map +1 -1
- package/dist/remote/index.d.ts +1 -1
- package/dist/remote/index.js.map +1 -1
- package/dist/remote/oauth.d.ts +1 -1
- package/dist/remote/oauth.js +1 -1
- package/dist/remote/oauth.js.map +1 -1
- package/dist/route.d.ts +40 -2
- package/dist/route.js +128 -8
- package/dist/route.js.map +1 -1
- package/dist/start.js +9 -4
- package/dist/start.js.map +1 -1
- package/dist/suggest.js +1 -1
- package/dist/suggest.js.map +1 -1
- package/dist/typegen.d.ts +27 -0
- package/dist/typegen.js +62 -0
- package/dist/typegen.js.map +1 -0
- package/dist/version-constant.d.ts +5 -0
- package/dist/version-constant.js +6 -0
- package/dist/version-constant.js.map +1 -0
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/dist/version.js.map +1 -1
- package/package.json +12 -2
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { DEV_AUDIT_PATH } from "../cli/dev-audit.js";
|
|
2
|
+
import { readDevLock } from "../cli/dev-lock.js";
|
|
3
|
+
export function auditChannel() {
|
|
4
|
+
const listeners = new Set();
|
|
5
|
+
return {
|
|
6
|
+
push(event) {
|
|
7
|
+
for (const listener of listeners)
|
|
8
|
+
listener(event);
|
|
9
|
+
},
|
|
10
|
+
subscribe(listener) {
|
|
11
|
+
listeners.add(listener);
|
|
12
|
+
return () => void listeners.delete(listener);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* mext dev 의 감사 tap endpoint 를 읽는 출처. 구독자가 있는 동안 연결 하나를 유지하고, 끊기면 ?after 로 이어 받는다.
|
|
18
|
+
* 토큰은 Authorization 헤더로만 보낸다 (출력하지 않는다)
|
|
19
|
+
*/
|
|
20
|
+
export function devAuditSource(options) {
|
|
21
|
+
const doFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
22
|
+
const credentials = () => (typeof options.credentials === "function" ? options.credentials() : options.credentials);
|
|
23
|
+
const listeners = new Set();
|
|
24
|
+
let controller;
|
|
25
|
+
let last = 0;
|
|
26
|
+
let lastToken;
|
|
27
|
+
const pump = async (signal) => {
|
|
28
|
+
let wait = options.retryMs ?? 500;
|
|
29
|
+
while (!signal.aborted) {
|
|
30
|
+
try {
|
|
31
|
+
const current = credentials();
|
|
32
|
+
if (!current)
|
|
33
|
+
throw new Error("mext dev is not running");
|
|
34
|
+
// 새 mext dev 실행 (토큰이 바뀜) = seq 가 1부터 다시
|
|
35
|
+
if (current.token !== lastToken)
|
|
36
|
+
last = 0;
|
|
37
|
+
lastToken = current.token;
|
|
38
|
+
const url = new URL(DEV_AUDIT_PATH, current.url);
|
|
39
|
+
if (last > 0)
|
|
40
|
+
url.searchParams.set("after", String(last));
|
|
41
|
+
const response = await doFetch(url, { headers: { Authorization: `Bearer ${current.token}` }, signal });
|
|
42
|
+
if (!response.ok || !response.body) {
|
|
43
|
+
await response.body?.cancel();
|
|
44
|
+
throw new Error(`HTTP ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
wait = options.retryMs ?? 500;
|
|
47
|
+
const decoder = new TextDecoder();
|
|
48
|
+
let buffer = "";
|
|
49
|
+
for await (const chunk of response.body) {
|
|
50
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
51
|
+
let i;
|
|
52
|
+
while ((i = buffer.indexOf("\n")) >= 0) {
|
|
53
|
+
const text = buffer.slice(0, i);
|
|
54
|
+
buffer = buffer.slice(i + 1);
|
|
55
|
+
if (text === "")
|
|
56
|
+
continue;
|
|
57
|
+
const line = JSON.parse(text);
|
|
58
|
+
if (typeof line.seq === "number")
|
|
59
|
+
last = line.seq;
|
|
60
|
+
for (const listener of listeners)
|
|
61
|
+
listener(line.event);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// 연결 실패 · 끊김 = 잠시 뒤 다시
|
|
67
|
+
}
|
|
68
|
+
if (signal.aborted)
|
|
69
|
+
return;
|
|
70
|
+
await new Promise((resolve) => {
|
|
71
|
+
const timer = setTimeout(resolve, wait);
|
|
72
|
+
signal.addEventListener("abort", () => (clearTimeout(timer), resolve()), { once: true });
|
|
73
|
+
});
|
|
74
|
+
wait = Math.min(wait * 2, 5_000);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
subscribe(listener) {
|
|
79
|
+
listeners.add(listener);
|
|
80
|
+
if (!controller) {
|
|
81
|
+
controller = new AbortController();
|
|
82
|
+
void pump(controller.signal);
|
|
83
|
+
}
|
|
84
|
+
return () => {
|
|
85
|
+
listeners.delete(listener);
|
|
86
|
+
if (listeners.size === 0) {
|
|
87
|
+
controller?.abort();
|
|
88
|
+
controller = undefined;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 대상 서버의 감사 출처. 대상이 이 프로젝트의 mext dev (대상 URL = .mext/dev/lock 의 url) 일 때만 자동 연결.
|
|
96
|
+
* 그 외 = 사용 불가 + 이유 (프로그램 API 로 출처를 직접 연결할 수 있다)
|
|
97
|
+
*/
|
|
98
|
+
export function resolveAudit(target, cwd) {
|
|
99
|
+
const lock = readDevLock(cwd);
|
|
100
|
+
if (!lock?.url || !sameEndpoint(lock.url, target.url)) {
|
|
101
|
+
return {
|
|
102
|
+
available: false,
|
|
103
|
+
reason: "Audit events are only streamed from the mext dev server of this project. For other servers (remote, mext start), " +
|
|
104
|
+
'connect an audit source in code: createInspector({ audit }) from "@obov/mext/inspect".',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (lock.auditToken === undefined) {
|
|
108
|
+
return { available: false, reason: "The running mext dev does not expose audit events (started by an older mext). Restart mext dev." };
|
|
109
|
+
}
|
|
110
|
+
const url = lock.url;
|
|
111
|
+
return {
|
|
112
|
+
available: true,
|
|
113
|
+
source: devAuditSource({
|
|
114
|
+
credentials: () => {
|
|
115
|
+
const now = readDevLock(cwd);
|
|
116
|
+
return now?.url && now.auditToken !== undefined && sameEndpoint(now.url, url) ? { url: now.url, token: now.auditToken } : undefined;
|
|
117
|
+
},
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
122
|
+
function sameEndpoint(a, b) {
|
|
123
|
+
try {
|
|
124
|
+
const x = new URL(a);
|
|
125
|
+
const y = new URL(b);
|
|
126
|
+
const host = (u) => (LOOPBACK.has(u.hostname) ? "loopback" : u.hostname);
|
|
127
|
+
return x.protocol === y.protocol && host(x) === host(y) && x.port === y.port && x.pathname === y.pathname;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/inspect/audit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAqB,MAAM,qBAAqB,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAiBhD,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAA;IACxD,OAAO;QACL,IAAI,CAAC,KAAK;YACR,KAAK,MAAM,QAAQ,IAAI,SAAS;gBAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;QACnD,CAAC;QACD,SAAS,CAAC,QAAQ;YAChB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACvB,OAAO,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC9C,CAAC;KACF,CAAA;AACH,CAAC;AAiBD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,OAAO,GAAiB,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IACpF,MAAM,WAAW,GAAG,GAAoC,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACpJ,MAAM,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAA;IACxD,IAAI,UAAuC,CAAA;IAC3C,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,SAA6B,CAAA;IAEjC,MAAM,IAAI,GAAG,KAAK,EAAE,MAAmB,EAAiB,EAAE;QACxD,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,GAAG,CAAA;QACjC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,WAAW,EAAE,CAAA;gBAC7B,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;gBACxD,wCAAwC;gBACxC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;oBAAE,IAAI,GAAG,CAAC,CAAA;gBACzC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAA;gBACzB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;gBAChD,IAAI,IAAI,GAAG,CAAC;oBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;gBACzD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;gBACtG,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACnC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;oBAC7B,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;gBAC5C,CAAC;gBACD,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,GAAG,CAAA;gBAC7B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;gBACjC,IAAI,MAAM,GAAG,EAAE,CAAA;gBACf,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,CAAC,IAA4C,EAAE,CAAC;oBAChF,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;oBACjD,IAAI,CAAS,CAAA;oBACb,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;wBAC/B,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;wBAC5B,IAAI,IAAI,KAAK,EAAE;4BAAE,SAAQ;wBACzB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAA;wBAC7C,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ;4BAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAA;wBACjD,KAAK,MAAM,QAAQ,IAAI,SAAS;4BAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBACxD,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,uBAAuB;YACzB,CAAC;YACD,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAM;YAC1B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;gBACvC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;YAC1F,CAAC,CAAC,CAAA;YACF,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QAClC,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,SAAS,CAAC,QAAQ;YAChB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACvB,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;gBAClC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;YAC9B,CAAC;YACD,OAAO,GAAG,EAAE;gBACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;gBAC1B,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBACzB,UAAU,EAAE,KAAK,EAAE,CAAA;oBACnB,UAAU,GAAG,SAAS,CAAA;gBACxB,CAAC;YACH,CAAC,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAID;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAuB,EAAE,GAAW;IAC/D,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EACJ,mHAAmH;gBACnH,wFAAwF;SAC3F,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,iGAAiG,EAAE,CAAA;IACxI,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;IACpB,OAAO;QACL,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,cAAc,CAAC;YACrB,WAAW,EAAE,GAAG,EAAE;gBAChB,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;gBAC5B,OAAO,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;YACrI,CAAC;SACF,CAAC;KACH,CAAA;AACH,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAA;AAE7D,SAAS,YAAY,CAAC,CAAS,EAAE,CAAS;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,IAAI,GAAG,CAAC,CAAM,EAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;QACrF,OAAO,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAA;IAC3G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC","sourcesContent":["import type { AuditEvent } from \"../audit.ts\"\nimport { DEV_AUDIT_PATH, type DevAuditLine } from \"../cli/dev-audit.ts\"\nimport { readDevLock } from \"../cli/dev-lock.ts\"\n\n/**\n * 감사 출처 (specs/inspector.md §6): 서버의 감사 이벤트 (mext.md §11.1) 를 Inspector 로 잇는다.\n * mext dev = 감사 tap 의 endpoint (mext.md §10). 그 외 = 호출 코드가 출처를 만들어 연결한다\n */\n\nexport interface AuditSource {\n /** 이벤트마다 listener 호출. 반환 = 구독 해제 */\n subscribe(listener: (event: AuditEvent) => void): () => void\n}\n\n/** 같은 프로세스의 서버와 잇는 출처: mext.config.ts · startMcpHttpServer 의 audit.sink 에 push 를 넘긴다 */\nexport interface AuditChannel extends AuditSource {\n push(event: AuditEvent): void\n}\n\nexport function auditChannel(): AuditChannel {\n const listeners = new Set<(event: AuditEvent) => void>()\n return {\n push(event) {\n for (const listener of listeners) listener(event)\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => void listeners.delete(listener)\n },\n }\n}\n\nexport interface DevAuditCredentials {\n /** mext dev 의 MCP 엔드포인트 (lock 의 url) */\n url: string\n /** lock 의 auditToken */\n token: string\n}\n\nexport interface DevAuditSourceOptions {\n /** 연결할 때마다 읽는다 (mext dev 를 다시 시작하면 토큰이 바뀐다). undefined = 실행 중인 dev 없음 → 잠시 뒤 다시 */\n credentials: DevAuditCredentials | (() => DevAuditCredentials | undefined)\n fetch?: typeof fetch\n /** 다시 연결하기 전 대기 (기본 500ms, 실패가 이어지면 5초까지 늘어난다) */\n retryMs?: number\n}\n\n/**\n * mext dev 의 감사 tap endpoint 를 읽는 출처. 구독자가 있는 동안 연결 하나를 유지하고, 끊기면 ?after 로 이어 받는다.\n * 토큰은 Authorization 헤더로만 보낸다 (출력하지 않는다)\n */\nexport function devAuditSource(options: DevAuditSourceOptions): AuditSource {\n const doFetch: typeof fetch = options.fetch ?? ((input, init) => fetch(input, init))\n const credentials = (): DevAuditCredentials | undefined => (typeof options.credentials === \"function\" ? options.credentials() : options.credentials)\n const listeners = new Set<(event: AuditEvent) => void>()\n let controller: AbortController | undefined\n let last = 0\n let lastToken: string | undefined\n\n const pump = async (signal: AbortSignal): Promise<void> => {\n let wait = options.retryMs ?? 500\n while (!signal.aborted) {\n try {\n const current = credentials()\n if (!current) throw new Error(\"mext dev is not running\")\n // 새 mext dev 실행 (토큰이 바뀜) = seq 가 1부터 다시\n if (current.token !== lastToken) last = 0\n lastToken = current.token\n const url = new URL(DEV_AUDIT_PATH, current.url)\n if (last > 0) url.searchParams.set(\"after\", String(last))\n const response = await doFetch(url, { headers: { Authorization: `Bearer ${current.token}` }, signal })\n if (!response.ok || !response.body) {\n await response.body?.cancel()\n throw new Error(`HTTP ${response.status}`)\n }\n wait = options.retryMs ?? 500\n const decoder = new TextDecoder()\n let buffer = \"\"\n for await (const chunk of response.body as unknown as AsyncIterable<Uint8Array>) {\n buffer += decoder.decode(chunk, { stream: true })\n let i: number\n while ((i = buffer.indexOf(\"\\n\")) >= 0) {\n const text = buffer.slice(0, i)\n buffer = buffer.slice(i + 1)\n if (text === \"\") continue\n const line = JSON.parse(text) as DevAuditLine\n if (typeof line.seq === \"number\") last = line.seq\n for (const listener of listeners) listener(line.event)\n }\n }\n } catch {\n // 연결 실패 · 끊김 = 잠시 뒤 다시\n }\n if (signal.aborted) return\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, wait)\n signal.addEventListener(\"abort\", () => (clearTimeout(timer), resolve()), { once: true })\n })\n wait = Math.min(wait * 2, 5_000)\n }\n }\n\n return {\n subscribe(listener) {\n listeners.add(listener)\n if (!controller) {\n controller = new AbortController()\n void pump(controller.signal)\n }\n return () => {\n listeners.delete(listener)\n if (listeners.size === 0) {\n controller?.abort()\n controller = undefined\n }\n }\n },\n }\n}\n\nexport type AuditAvailability = { available: true; source: AuditSource } | { available: false; reason: string }\n\n/**\n * 대상 서버의 감사 출처. 대상이 이 프로젝트의 mext dev (대상 URL = .mext/dev/lock 의 url) 일 때만 자동 연결.\n * 그 외 = 사용 불가 + 이유 (프로그램 API 로 출처를 직접 연결할 수 있다)\n */\nexport function resolveAudit(target: { url: string }, cwd: string): AuditAvailability {\n const lock = readDevLock(cwd)\n if (!lock?.url || !sameEndpoint(lock.url, target.url)) {\n return {\n available: false,\n reason:\n \"Audit events are only streamed from the mext dev server of this project. For other servers (remote, mext start), \" +\n 'connect an audit source in code: createInspector({ audit }) from \"@obov/mext/inspect\".',\n }\n }\n if (lock.auditToken === undefined) {\n return { available: false, reason: \"The running mext dev does not expose audit events (started by an older mext). Restart mext dev.\" }\n }\n const url = lock.url\n return {\n available: true,\n source: devAuditSource({\n credentials: () => {\n const now = readDevLock(cwd)\n return now?.url && now.auditToken !== undefined && sameEndpoint(now.url, url) ? { url: now.url, token: now.auditToken } : undefined\n },\n }),\n }\n}\n\nconst LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\"])\n\nfunction sameEndpoint(a: string, b: string): boolean {\n try {\n const x = new URL(a)\n const y = new URL(b)\n const host = (u: URL): string => (LOOPBACK.has(u.hostname) ? \"loopback\" : u.hostname)\n return x.protocol === y.protocol && host(x) === host(y) && x.port === y.port && x.pathname === y.pathname\n } catch {\n return false\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 기대와 실제의 일부 비교 (specs/inspector.md §4 expect). 시나리오 · 에이전트 단계가 같이 쓴다 */
|
|
2
|
+
export interface Failure {
|
|
3
|
+
/** 다른 곳 (ok · value.status · error.code · commands.hidden · input · save.<변수> · audit.<n> · none.<n> · agent) */
|
|
4
|
+
path: string;
|
|
5
|
+
expected: unknown;
|
|
6
|
+
actual: unknown;
|
|
7
|
+
}
|
|
8
|
+
/** 일부 비교: 객체 = 적은 키만 (재귀) · 배열 = 길이와 각 요소 · 그 외 = 같은 값 */
|
|
9
|
+
export declare function partial(expected: unknown, actual: unknown, path: string, failures: Failure[]): void;
|
|
10
|
+
/** 일부 비교가 통과하는가 */
|
|
11
|
+
export declare function matches(expected: unknown, actual: unknown): boolean;
|
|
12
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** 기대와 실제의 일부 비교 (specs/inspector.md §4 expect). 시나리오 · 에이전트 단계가 같이 쓴다 */
|
|
2
|
+
/** 일부 비교: 객체 = 적은 키만 (재귀) · 배열 = 길이와 각 요소 · 그 외 = 같은 값 */
|
|
3
|
+
export function partial(expected, actual, path, failures) {
|
|
4
|
+
if (Array.isArray(expected)) {
|
|
5
|
+
if (!Array.isArray(actual))
|
|
6
|
+
return void failures.push({ path, expected, actual });
|
|
7
|
+
if (actual.length !== expected.length)
|
|
8
|
+
return void failures.push({ path: `${path}.length`, expected: expected.length, actual: actual.length });
|
|
9
|
+
expected.forEach((item, i) => partial(item, actual[i], `${path}.${i}`, failures));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (isRecord(expected)) {
|
|
13
|
+
if (!isRecord(actual))
|
|
14
|
+
return void failures.push({ path, expected, actual });
|
|
15
|
+
for (const [key, value] of Object.entries(expected))
|
|
16
|
+
partial(value, actual[key], `${path}.${key}`, failures);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!Object.is(expected, actual))
|
|
20
|
+
failures.push({ path, expected, actual });
|
|
21
|
+
}
|
|
22
|
+
/** 일부 비교가 통과하는가 */
|
|
23
|
+
export function matches(expected, actual) {
|
|
24
|
+
const failures = [];
|
|
25
|
+
partial(expected, actual, "", failures);
|
|
26
|
+
return failures.length === 0;
|
|
27
|
+
}
|
|
28
|
+
export function isRecord(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/inspect/compare.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAS1E,0DAA0D;AAC1D,MAAM,UAAU,OAAO,CAAC,QAAiB,EAAE,MAAe,EAAE,IAAY,EAAE,QAAmB;IAC3F,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;QACjF,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;YAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QAC9I,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;QACjF,OAAM;IACR,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;QAC5E,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;QAC5G,OAAM;IACR,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;AAC7E,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,OAAO,CAAC,QAAiB,EAAE,MAAe;IACxD,MAAM,QAAQ,GAAc,EAAE,CAAA;IAC9B,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAA;IACvC,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC","sourcesContent":["/** 기대와 실제의 일부 비교 (specs/inspector.md §4 expect). 시나리오 · 에이전트 단계가 같이 쓴다 */\n\nexport interface Failure {\n /** 다른 곳 (ok · value.status · error.code · commands.hidden · input · save.<변수> · audit.<n> · none.<n> · agent) */\n path: string\n expected: unknown\n actual: unknown\n}\n\n/** 일부 비교: 객체 = 적은 키만 (재귀) · 배열 = 길이와 각 요소 · 그 외 = 같은 값 */\nexport function partial(expected: unknown, actual: unknown, path: string, failures: Failure[]): void {\n if (Array.isArray(expected)) {\n if (!Array.isArray(actual)) return void failures.push({ path, expected, actual })\n if (actual.length !== expected.length) return void failures.push({ path: `${path}.length`, expected: expected.length, actual: actual.length })\n expected.forEach((item, i) => partial(item, actual[i], `${path}.${i}`, failures))\n return\n }\n if (isRecord(expected)) {\n if (!isRecord(actual)) return void failures.push({ path, expected, actual })\n for (const [key, value] of Object.entries(expected)) partial(value, actual[key], `${path}.${key}`, failures)\n return\n }\n if (!Object.is(expected, actual)) failures.push({ path, expected, actual })\n}\n\n/** 일부 비교가 통과하는가 */\nexport function matches(expected: unknown, actual: unknown): boolean {\n const failures: Failure[] = []\n partial(expected, actual, \"\", failures)\n return failures.length === 0\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @obov/mext/inspect — inspector 의 프로그램 API (specs/inspector.md). Node.js · Bun 전용 (파일 · 환경변수를 읽는다).
|
|
3
|
+
* mext inspect CLI 가 쓰는 것과 같은 객체: 호출자 프로필 · Inspector (프로필별 호출 · 호출 기록) · 시나리오.
|
|
4
|
+
* @obov/mext (.) 와 mext start · 고객 CLI 는 이 entry 를 import 하지 않는다
|
|
5
|
+
*/
|
|
6
|
+
export { AGENT_SETTLE_MS, AGENT_TIMEOUT_MS, checkAudit, connectAgent, instructAgent, isAgentClient, summarizeAudit } from "./agent.ts";
|
|
7
|
+
export type { AgentClient, AgentEvent, AgentExpect, AgentMessage, AgentObservation, AgentPlugin, AgentPluginContext, AgentToolCall, InstructOptions, } from "./agent.ts";
|
|
8
|
+
export { auditChannel, devAuditSource, resolveAudit } from "./audit.ts";
|
|
9
|
+
export type { AuditAvailability, AuditChannel, AuditSource, DevAuditCredentials, DevAuditSourceOptions } from "./audit.ts";
|
|
10
|
+
export { startUiServer, UI_DEFAULT_PORT, UI_TOKEN_HEADER } from "./ui-server.ts";
|
|
11
|
+
export type { UiServer, UiServerOptions } from "./ui-server.ts";
|
|
12
|
+
export { createInspector, InspectUsageError, outcomeOf, redactValue, resolveTarget } from "./inspector.ts";
|
|
13
|
+
export type { Answers, CallOutcome, CallRecord, ErrorBody, Exchange, InputRecord, InspectCallOptions, Inspector, InspectorEvent, InspectorOptions, InspectTarget, } from "./inspector.ts";
|
|
14
|
+
export { DEFAULT_PROFILE, DEFAULT_TOKEN_ENV, describeProfiles, findProfile, INSPECT_DIR, loadProfiles, parseProfiles, ProfileFileError, PROFILES_FILE, profilesPath, ProfileUnavailableError, redact, REDACTED, resolveProfile, } from "./profiles.ts";
|
|
15
|
+
export type { EnvRef, Profile, ProfileSet, ProfileView, ResolvedProfile } from "./profiles.ts";
|
|
16
|
+
export { appendStep, compare, loadScenario, parseScenario, runScenario, ScenarioFileError, scenarioFiles, SCENARIOS_DIR, stepFromRecord, } from "./scenario.ts";
|
|
17
|
+
export type { AgentStep, CallStep, CommandsStep, Expect, Failure, RunScenarioOptions, Scenario, ScenarioResult, ScenarioStep, StepResult, } from "./scenario.ts";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @obov/mext/inspect — inspector 의 프로그램 API (specs/inspector.md). Node.js · Bun 전용 (파일 · 환경변수를 읽는다).
|
|
3
|
+
* mext inspect CLI 가 쓰는 것과 같은 객체: 호출자 프로필 · Inspector (프로필별 호출 · 호출 기록) · 시나리오.
|
|
4
|
+
* @obov/mext (.) 와 mext start · 고객 CLI 는 이 entry 를 import 하지 않는다
|
|
5
|
+
*/
|
|
6
|
+
export { AGENT_SETTLE_MS, AGENT_TIMEOUT_MS, checkAudit, connectAgent, instructAgent, isAgentClient, summarizeAudit } from "./agent.js";
|
|
7
|
+
export { auditChannel, devAuditSource, resolveAudit } from "./audit.js";
|
|
8
|
+
export { startUiServer, UI_DEFAULT_PORT, UI_TOKEN_HEADER } from "./ui-server.js";
|
|
9
|
+
export { createInspector, InspectUsageError, outcomeOf, redactValue, resolveTarget } from "./inspector.js";
|
|
10
|
+
export { DEFAULT_PROFILE, DEFAULT_TOKEN_ENV, describeProfiles, findProfile, INSPECT_DIR, loadProfiles, parseProfiles, ProfileFileError, PROFILES_FILE, profilesPath, ProfileUnavailableError, redact, REDACTED, resolveProfile, } from "./profiles.js";
|
|
11
|
+
export { appendStep, compare, loadScenario, parseScenario, runScenario, ScenarioFileError, scenarioFiles, SCENARIOS_DIR, stepFromRecord, } from "./scenario.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/inspect/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAYtI,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEvE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAEhF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAc1G,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,MAAM,EACN,QAAQ,EACR,cAAc,GACf,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,UAAU,EACV,OAAO,EACP,YAAY,EACZ,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,eAAe,CAAA","sourcesContent":["/**\n * @obov/mext/inspect — inspector 의 프로그램 API (specs/inspector.md). Node.js · Bun 전용 (파일 · 환경변수를 읽는다).\n * mext inspect CLI 가 쓰는 것과 같은 객체: 호출자 프로필 · Inspector (프로필별 호출 · 호출 기록) · 시나리오.\n * @obov/mext (.) 와 mext start · 고객 CLI 는 이 entry 를 import 하지 않는다\n */\nexport { AGENT_SETTLE_MS, AGENT_TIMEOUT_MS, checkAudit, connectAgent, instructAgent, isAgentClient, summarizeAudit } from \"./agent.ts\"\nexport type {\n AgentClient,\n AgentEvent,\n AgentExpect,\n AgentMessage,\n AgentObservation,\n AgentPlugin,\n AgentPluginContext,\n AgentToolCall,\n InstructOptions,\n} from \"./agent.ts\"\nexport { auditChannel, devAuditSource, resolveAudit } from \"./audit.ts\"\nexport type { AuditAvailability, AuditChannel, AuditSource, DevAuditCredentials, DevAuditSourceOptions } from \"./audit.ts\"\nexport { startUiServer, UI_DEFAULT_PORT, UI_TOKEN_HEADER } from \"./ui-server.ts\"\nexport type { UiServer, UiServerOptions } from \"./ui-server.ts\"\nexport { createInspector, InspectUsageError, outcomeOf, redactValue, resolveTarget } from \"./inspector.ts\"\nexport type {\n Answers,\n CallOutcome,\n CallRecord,\n ErrorBody,\n Exchange,\n InputRecord,\n InspectCallOptions,\n Inspector,\n InspectorEvent,\n InspectorOptions,\n InspectTarget,\n} from \"./inspector.ts\"\nexport {\n DEFAULT_PROFILE,\n DEFAULT_TOKEN_ENV,\n describeProfiles,\n findProfile,\n INSPECT_DIR,\n loadProfiles,\n parseProfiles,\n ProfileFileError,\n PROFILES_FILE,\n profilesPath,\n ProfileUnavailableError,\n redact,\n REDACTED,\n resolveProfile,\n} from \"./profiles.ts\"\nexport type { EnvRef, Profile, ProfileSet, ProfileView, ResolvedProfile } from \"./profiles.ts\"\nexport {\n appendStep,\n compare,\n loadScenario,\n parseScenario,\n runScenario,\n ScenarioFileError,\n scenarioFiles,\n SCENARIOS_DIR,\n stepFromRecord,\n} from \"./scenario.ts\"\nexport type {\n AgentStep,\n CallStep,\n CommandsStep,\n Expect,\n Failure,\n RunScenarioOptions,\n Scenario,\n ScenarioResult,\n ScenarioStep,\n StepResult,\n} from \"./scenario.ts\"\n"]}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { AuditEvent } from "../audit.ts";
|
|
2
|
+
import { CallError } from "../client/typed.ts";
|
|
3
|
+
import type { CommandInfo, CommandList, InputAnswer, InputRequestInfo, ProgressUpdate, ServerInfo, TaskStatus } from "../client/types.ts";
|
|
4
|
+
import type { AuditSource } from "./audit.ts";
|
|
5
|
+
import { type Profile, type ProfileSet, type ProfileView } from "./profiles.ts";
|
|
6
|
+
/**
|
|
7
|
+
* Inspector (specs/inspector.md): 호출자 프로필별 타입 client · 호출 기록 (원시 요청 · 응답, 비밀 값 가림).
|
|
8
|
+
* CLI · 웹 UI · 시나리오 실행이 같은 객체를 쓴다. 호출 코드는 타입 client 하나 (별도 MCP 클라이언트 구현 없음)
|
|
9
|
+
*/
|
|
10
|
+
export interface InspectTarget {
|
|
11
|
+
url: string;
|
|
12
|
+
/** flag = --url · env = MEXT_URL · dev = 이 프로젝트의 mext dev (.mext/dev/lock) */
|
|
13
|
+
source: "flag" | "env" | "dev";
|
|
14
|
+
}
|
|
15
|
+
/** 대상 서버를 정할 수 없다 · URL 형식 오류 · 없는 프로필 (CLI = exit 2) */
|
|
16
|
+
export declare class InspectUsageError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
/** --url > MEXT_URL > 실행 중인 mext dev. 셋 다 없음 = InspectUsageError */
|
|
20
|
+
export declare function resolveTarget(options: {
|
|
21
|
+
url?: string;
|
|
22
|
+
env?: Record<string, string | undefined>;
|
|
23
|
+
cwd: string;
|
|
24
|
+
}): InspectTarget;
|
|
25
|
+
/** HTTP 왕복 하나 (원시 요청 · 응답). 비밀 값은 [redacted] */
|
|
26
|
+
export interface Exchange {
|
|
27
|
+
/** JSON-RPC method (tools/call · tools/list · server/discover · tasks/get …) */
|
|
28
|
+
rpc?: string;
|
|
29
|
+
request: {
|
|
30
|
+
method: string;
|
|
31
|
+
url: string;
|
|
32
|
+
headers: Record<string, string>;
|
|
33
|
+
body: unknown;
|
|
34
|
+
};
|
|
35
|
+
/** body = JSON 응답 · SSE 메시지 배열 (text/event-stream) · 그 외 텍스트 */
|
|
36
|
+
response?: {
|
|
37
|
+
status: number;
|
|
38
|
+
headers: Record<string, string>;
|
|
39
|
+
body: unknown;
|
|
40
|
+
};
|
|
41
|
+
/** 응답을 받지 못함 (연결 실패 · 취소) */
|
|
42
|
+
error?: string;
|
|
43
|
+
durationMs: number;
|
|
44
|
+
}
|
|
45
|
+
/** 오류 본문 { code, message, ...details } */
|
|
46
|
+
export type ErrorBody = Record<string, unknown> & {
|
|
47
|
+
code: string;
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
export type CallOutcome = {
|
|
51
|
+
ok: true;
|
|
52
|
+
value: unknown;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
error: ErrorBody;
|
|
56
|
+
};
|
|
57
|
+
export interface InputRecord {
|
|
58
|
+
kind: InputRequestInfo["kind"];
|
|
59
|
+
message: string;
|
|
60
|
+
/** 보낸 답 (true · false · { action, content }). 답 없음 = undefined */
|
|
61
|
+
answer?: InputAnswer;
|
|
62
|
+
}
|
|
63
|
+
/** 호출 하나의 기록 */
|
|
64
|
+
export interface CallRecord {
|
|
65
|
+
/** 이 Inspector 안에서 1부터 */
|
|
66
|
+
id: number;
|
|
67
|
+
/** 시작 시각 (ISO) */
|
|
68
|
+
at: string;
|
|
69
|
+
profile: string;
|
|
70
|
+
command: string;
|
|
71
|
+
input: Record<string, unknown>;
|
|
72
|
+
confirmed: boolean;
|
|
73
|
+
ok: boolean;
|
|
74
|
+
/** ok = 반환값 */
|
|
75
|
+
value?: unknown;
|
|
76
|
+
/** ok 아님 = 오류 본문 */
|
|
77
|
+
error?: ErrorBody;
|
|
78
|
+
durationMs: number;
|
|
79
|
+
progress: ProgressUpdate[];
|
|
80
|
+
inputs: InputRecord[];
|
|
81
|
+
/** task 로 실행됐으면 마지막 상태 */
|
|
82
|
+
task?: TaskStatus;
|
|
83
|
+
/**
|
|
84
|
+
* 이 호출의 tools/call 요청에 실은 trace ID (params._meta.traceparent, W3C trace context).
|
|
85
|
+
* 서버의 감사 이벤트 traceId 와 같다 = 호출과 이벤트를 잇는 값
|
|
86
|
+
*/
|
|
87
|
+
traceId: string;
|
|
88
|
+
exchanges: Exchange[];
|
|
89
|
+
}
|
|
90
|
+
/** 입력 요청의 답 (종류별). 적지 않은 종류 = INPUT_UNAVAILABLE */
|
|
91
|
+
export interface Answers {
|
|
92
|
+
confirm?: boolean;
|
|
93
|
+
/** 값 객체 = 수락 · false = 거절 */
|
|
94
|
+
form?: Record<string, unknown> | false;
|
|
95
|
+
url?: boolean;
|
|
96
|
+
}
|
|
97
|
+
export interface InspectCallOptions {
|
|
98
|
+
/** 프로필 이름. 생략 = 프로필 파일의 default */
|
|
99
|
+
profile?: string;
|
|
100
|
+
/** 사람이 확인했다는 표시 (client.md §2.2): 확인 표시 전송 + confirm 입력 요청 승인 */
|
|
101
|
+
confirmed?: boolean;
|
|
102
|
+
answers?: Answers;
|
|
103
|
+
/** 입력 · 확인 요청을 모두 거절 */
|
|
104
|
+
decline?: boolean;
|
|
105
|
+
/** 입력 요청을 직접 처리 (웹 UI). answers · decline 보다 먼저 */
|
|
106
|
+
onInput?: (request: InputRequestInfo) => InputAnswer | Promise<InputAnswer>;
|
|
107
|
+
onProgress?: (update: ProgressUpdate) => void;
|
|
108
|
+
onTask?: (task: TaskStatus) => void;
|
|
109
|
+
signal?: AbortSignal;
|
|
110
|
+
}
|
|
111
|
+
export interface InspectorOptions {
|
|
112
|
+
/** 대상 서버의 MCP 엔드포인트 */
|
|
113
|
+
url: string;
|
|
114
|
+
profiles: ProfileSet;
|
|
115
|
+
/** 프로필 값을 읽을 환경변수 (기본 process.env) */
|
|
116
|
+
env?: Record<string, string | undefined>;
|
|
117
|
+
fetch?: typeof fetch;
|
|
118
|
+
/** 보관할 호출 기록 수 (기본 200. 넘으면 오래된 것부터 버린다) */
|
|
119
|
+
historyLimit?: number;
|
|
120
|
+
/** 호출 기록이 추가될 때 */
|
|
121
|
+
onRecord?: (record: CallRecord) => void;
|
|
122
|
+
/** 감사 출처 (§6). 없음 = 감사 이벤트 없음 */
|
|
123
|
+
audit?: AuditSource;
|
|
124
|
+
/** 보관할 감사 이벤트 수 (기본 500) */
|
|
125
|
+
auditLimit?: number;
|
|
126
|
+
}
|
|
127
|
+
/** Inspector 가 알리는 일: 호출 기록 추가 · 감사 이벤트 수신 */
|
|
128
|
+
export type InspectorEvent = {
|
|
129
|
+
type: "record";
|
|
130
|
+
record: CallRecord;
|
|
131
|
+
} | {
|
|
132
|
+
type: "audit";
|
|
133
|
+
event: AuditEvent;
|
|
134
|
+
};
|
|
135
|
+
export interface Inspector {
|
|
136
|
+
readonly url: string;
|
|
137
|
+
readonly profileSet: ProfileSet;
|
|
138
|
+
/** 표시용 프로필 목록 (값 없음) */
|
|
139
|
+
profiles(): ProfileView[];
|
|
140
|
+
/** 이름 확인 (생략 = default). 없는 이름 = InspectUsageError */
|
|
141
|
+
profile(name?: string): Profile;
|
|
142
|
+
/** 그 프로필에게 보이는 목록 (매번 서버에서 다시 읽는다). 실패 = CallError · ProfileUnavailableError */
|
|
143
|
+
commands(profile?: string): Promise<CommandList>;
|
|
144
|
+
describe(id: string, profile?: string): Promise<CommandInfo>;
|
|
145
|
+
server(profile?: string): Promise<ServerInfo>;
|
|
146
|
+
/** 호출. 호출 오류 · 프로필 값 없음 · 취소도 던지지 않고 기록 (ok: false) 으로 돌려준다 */
|
|
147
|
+
call(id: string, input?: Record<string, unknown>, options?: InspectCallOptions): Promise<CallRecord>;
|
|
148
|
+
/** 오래된 것부터 */
|
|
149
|
+
history(): CallRecord[];
|
|
150
|
+
clearHistory(): void;
|
|
151
|
+
/** 감사 출처가 연결돼 있는지 */
|
|
152
|
+
readonly auditConnected: boolean;
|
|
153
|
+
/** 받은 감사 이벤트 (오래된 것부터, 프로필의 비밀 값은 [redacted]) */
|
|
154
|
+
auditEvents(): AuditEvent[];
|
|
155
|
+
/** 값 안의 문자열에서 지금 읽을 수 있는 모든 프로필의 비밀 값을 가린다 (기록 · 이벤트와 같은 규칙) */
|
|
156
|
+
redact<T>(value: T): T;
|
|
157
|
+
/** 호출 기록 · 감사 이벤트 구독. 반환 = 해제 */
|
|
158
|
+
subscribe(listener: (event: InspectorEvent) => void): () => void;
|
|
159
|
+
/** 감사 출처 연결 해제 */
|
|
160
|
+
close(): void;
|
|
161
|
+
}
|
|
162
|
+
export declare function createInspector(options: InspectorOptions): Inspector;
|
|
163
|
+
/** 결과 요약 (기록 → 성공 값 또는 오류 본문) */
|
|
164
|
+
export declare function outcomeOf(record: CallRecord): CallOutcome;
|
|
165
|
+
/** 오류 본문. code · message 를 앞에 (출력 · 기록에서 먼저 보이게) */
|
|
166
|
+
export declare function orderedBody(error: CallError): ErrorBody;
|
|
167
|
+
/** 값 안의 문자열에서 비밀 값을 가린다 (객체 · 배열 재귀) */
|
|
168
|
+
export declare function redactValue(value: unknown, secrets: readonly string[]): unknown;
|