@obov/mext 0.4.0 → 0.6.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/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 +2 -1
- package/dist/build-cli.js.map +1 -1
- package/dist/build.d.ts +2 -0
- package/dist/build.js +30 -3
- 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/build.js +6 -3
- 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 +24 -1
- 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.js +16 -4
- 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 +8 -0
- package/dist/cli/main.js.map +1 -1
- 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 +28 -4
- package/dist/cli/run.js.map +1 -1
- 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 +10 -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 +37 -3
- package/dist/client/runner.js.map +1 -1
- package/dist/client/typed.d.ts +54 -0
- package/dist/client/typed.js +490 -0
- package/dist/client/typed.js.map +1 -0
- package/dist/client/types.d.ts +196 -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 +42 -10
- package/dist/config.js +32 -7
- package/dist/config.js.map +1 -1
- 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 +15 -6
- package/dist/index.js +7 -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 +32 -5
- package/dist/invoke.js +150 -16
- package/dist/invoke.js.map +1 -1
- package/dist/manifest.d.ts +13 -2
- package/dist/manifest.js +5 -1
- package/dist/manifest.js.map +1 -1
- package/dist/mcp/constants.d.ts +5 -0
- package/dist/mcp/constants.js +5 -0
- package/dist/mcp/constants.js.map +1 -1
- package/dist/mcp/dispatch.d.ts +1 -1
- package/dist/mcp/dispatch.js +14 -3
- 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/http.d.ts +16 -2
- package/dist/mcp/http.js +76 -7
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/install.d.ts +1 -1
- package/dist/mcp/install.js +3 -2
- package/dist/mcp/install.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 +28 -0
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp/tasks.d.ts +3 -1
- package/dist/mcp/tasks.js +6 -3
- 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 +2 -0
- package/dist/remote/client.js +3 -2
- package/dist/remote/client.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/runtime.d.ts +10 -0
- package/dist/runtime.js +11 -0
- package/dist/runtime.js.map +1 -0
- package/dist/start.js +6 -2
- 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 +14 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario.js","sourceRoot":"","sources":["../../src/inspect/scenario.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\"\nimport { basename, dirname, join, resolve } from \"node:path\"\nimport type { AuditEvent } from \"../audit.ts\"\nimport { CallError } from \"../client/typed.ts\"\nimport { AGENT_TIMEOUT_MS, checkAudit, connectAgent, instructAgent, type AgentClient, type AgentExpect, type AgentObservation } from \"./agent.ts\"\nimport { isRecord, partial, type Failure } from \"./compare.ts\"\nimport { orderedBody, outcomeOf, type Answers, type CallOutcome, type CallRecord, type ErrorBody, type Inspector } from \"./inspector.ts\"\nimport { findProfile, INSPECT_DIR, ProfileUnavailableError, type ProfileSet } from \"./profiles.ts\"\n\n/**\n * 시나리오 (specs/inspector.md §4): 호출 순서 + 기대 결과를 적은 JSON 파일. 사람 · CLI · 웹 UI · 에이전트가 같은 파일을 쓴다.\n * 코드 실행 없음 (값 비교만)\n */\n\n/** <프로젝트>/inspect/scenarios */\nexport const SCENARIOS_DIR = \"scenarios\"\n\nexport interface Expect {\n /** true = 성공해야 한다 · false = 오류여야 한다 */\n ok?: boolean\n /** 성공 값의 일부 */\n value?: unknown\n /** 오류 code, 또는 오류 본문의 일부 */\n error?: string | Record<string, unknown>\n /** 이 호출의 감사 이벤트 목록 (trace ID 가 같은 것) 의 일부 비교 */\n audit?: Record<string, unknown>[]\n}\n\nexport interface CallStep {\n name?: string\n as?: string\n call: string\n input?: Record<string, unknown>\n confirmed?: boolean\n answers?: Answers\n decline?: boolean\n expect?: Expect\n /** 변수 → 성공 값의 점 구분 경로 */\n save?: Record<string, string>\n}\n\nexport interface CommandsStep {\n name?: string\n as?: string\n commands: { visible?: string[]; hidden?: string[] }\n}\n\n/** 에이전트에게 지시하고 서버의 감사 이벤트를 비교 (§7) */\nexport interface AgentStep {\n name?: string\n /** 지시문 ({{변수}} 치환) */\n agent: string\n /** 이 에이전트의 호출을 가려내는 조건 (이벤트의 일부) */\n from?: Record<string, unknown>\n expect?: AgentExpect\n timeoutMs?: number\n}\n\nexport type ScenarioStep = CallStep | CommandsStep | AgentStep\n\n/** agent 단계 결과의 as */\nexport const AGENT_AS = \"agent\"\n\nexport interface Scenario {\n /** 읽은 파일 (절대 경로) */\n file?: string\n name: string\n description?: string\n as?: string\n steps: ScenarioStep[]\n}\n\nexport type { Failure }\n\nexport interface StepResult {\n /** 1부터 */\n step: number\n name: string\n as: string\n pass: boolean\n durationMs: number\n /** call = 성공 값 또는 오류 본문 · commands = 보인 명령어 ID · agent = 관찰 (감사 이벤트 · 에이전트의 말) */\n outcome: CallOutcome | { ok: true; commands: string[] } | AgentObservation | undefined\n failures: Failure[]\n /** call 단계의 호출 기록 ID (Inspector.history) */\n record?: number\n}\n\nexport interface ScenarioResult {\n name: string\n file?: string\n pass: boolean\n durationMs: number\n steps: StepResult[]\n}\n\n/** 시나리오 파일 형식 오류. issues = \"<위치>: <문제>\" */\nexport class ScenarioFileError extends Error {\n readonly file: string\n readonly issues: readonly string[]\n constructor(file: string, issues: readonly string[]) {\n super(`${file}: invalid scenario file\\n${issues.map((i) => ` ${i}`).join(\"\\n\")}`)\n this.name = \"ScenarioFileError\"\n this.file = file\n this.issues = issues\n }\n}\n\n/** 경로 · 폴더 → 시나리오 파일 목록 (폴더 = 그 안의 *.json, 이름 순). 경로 없음 = <cwd>/inspect/scenarios */\nexport function scenarioFiles(cwd: string, paths: readonly string[]): string[] {\n const targets = paths.length > 0 ? paths.map((p) => resolve(cwd, p)) : [join(resolve(cwd), INSPECT_DIR, SCENARIOS_DIR)]\n const files: string[] = []\n for (const target of targets) {\n if (!existsSync(target)) throw new ScenarioFileError(target, [\"file not found\"])\n if (!statSync(target).isDirectory()) {\n files.push(target)\n continue\n }\n const found = readdirSync(target)\n .filter((name) => name.endsWith(\".json\"))\n .sort()\n .map((name) => join(target, name))\n if (found.length === 0) throw new ScenarioFileError(target, [\"no *.json scenario files in this folder\"])\n files.push(...found)\n }\n return files\n}\n\n/** 파일 읽기 + 검증. profiles 를 주면 없는 프로필 이름도 오류 */\nexport function loadScenario(file: string, profiles?: ProfileSet): Scenario {\n let value: unknown\n try {\n value = JSON.parse(readFileSync(file, \"utf8\"))\n } catch (error) {\n throw new ScenarioFileError(file, [`not valid JSON (${error instanceof Error ? error.message : String(error)})`])\n }\n return parseScenario(value, file, profiles)\n}\n\nconst SCENARIO_KEYS = [\"name\", \"description\", \"as\", \"steps\"]\nconst CALL_KEYS = [\"name\", \"as\", \"call\", \"input\", \"confirmed\", \"answers\", \"decline\", \"expect\", \"save\"]\nconst COMMANDS_KEYS = [\"name\", \"as\", \"commands\"]\nconst AGENT_KEYS = [\"name\", \"agent\", \"from\", \"expect\", \"timeoutMs\"]\n\n/** 시나리오 내용 검증 (문제를 모두 모아 한 번에) */\nexport function parseScenario(value: unknown, file: string, profiles?: ProfileSet): Scenario {\n if (!isRecord(value)) throw new ScenarioFileError(file, ['must be an object ({ \"name\": …, \"steps\": [ … ] })'])\n const issues: string[] = []\n const unknownKeys = (at: string, object: Record<string, unknown>, allowed: readonly string[]): void => {\n for (const key of Object.keys(object)) if (!allowed.includes(key)) issues.push(`${at}${key}: unknown key`)\n }\n const profileName = (at: string, as: unknown): string | undefined => {\n if (as === undefined) return undefined\n if (typeof as !== \"string\" || as === \"\") {\n issues.push(`${at}: must be a profile name`)\n return undefined\n }\n if (profiles && !findProfile(profiles, as)) {\n issues.push(`${at}: unknown profile \"${as}\" (profiles: ${profiles.profiles.map((p) => p.name).join(\", \")})`)\n }\n return as\n }\n /** 감사 이벤트의 일부 목록 */\n const eventPatterns = (at: string, list: unknown): Record<string, unknown>[] | undefined => {\n if (list === undefined) return undefined\n if (!Array.isArray(list) || !list.every(isRecord)) {\n issues.push(`${at}: must be an array of objects (parts of audit events)`)\n return undefined\n }\n return list as Record<string, unknown>[]\n }\n unknownKeys(\"\", value, SCENARIO_KEYS)\n const scenario: Scenario = { file, name: basename(file, \".json\"), steps: [] }\n if (value.name !== undefined) {\n if (typeof value.name !== \"string\" || value.name === \"\") issues.push(\"name: must be a non-empty string\")\n else scenario.name = value.name\n }\n if (value.description !== undefined) {\n if (typeof value.description !== \"string\") issues.push(\"description: must be a string\")\n else scenario.description = value.description\n }\n const as = profileName(\"as\", value.as)\n if (as !== undefined) scenario.as = as\n if (!Array.isArray(value.steps) || value.steps.length === 0) {\n issues.push(\"steps: must be a non-empty array\")\n } else {\n value.steps.forEach((raw: unknown, i) => {\n const at = `steps[${i + 1}]`\n if (!isRecord(raw)) return void issues.push(`${at}: must be an object`)\n const hasCall = raw.call !== undefined\n const hasCommands = raw.commands !== undefined\n const hasAgent = raw.agent !== undefined\n if (hasCall && hasCommands) return void issues.push(`${at}: use either \"call\" or \"commands\", not both`)\n if (hasAgent && (hasCall || hasCommands)) return void issues.push(`${at}: \"agent\" cannot be combined with \"call\" or \"commands\"`)\n if (!hasCall && !hasCommands && !hasAgent) return void issues.push(`${at}: needs \"call\" (a command ID), \"commands\", or \"agent\" (an instruction)`)\n const before = issues.length\n if (hasAgent) {\n unknownKeys(`${at}.`, raw, AGENT_KEYS)\n const step: AgentStep = { agent: \"\" }\n if (raw.name !== undefined) {\n if (typeof raw.name !== \"string\") issues.push(`${at}.name: must be a string`)\n else step.name = raw.name\n }\n if (typeof raw.agent !== \"string\" || raw.agent.trim() === \"\") issues.push(`${at}.agent: must be the instruction text`)\n else step.agent = raw.agent\n if (raw.from !== undefined) {\n if (!isRecord(raw.from)) issues.push(`${at}.from: must be an object (part of an audit event)`)\n else step.from = raw.from\n }\n if (raw.timeoutMs !== undefined) {\n if (typeof raw.timeoutMs !== \"number\" || !Number.isFinite(raw.timeoutMs) || raw.timeoutMs <= 0) issues.push(`${at}.timeoutMs: must be a positive number`)\n else step.timeoutMs = raw.timeoutMs\n }\n if (raw.expect !== undefined) {\n const e = raw.expect\n if (!isRecord(e)) issues.push(`${at}.expect: must be { \"audit\"?: [ … ], \"none\"?: [ … ] }`)\n else {\n unknownKeys(`${at}.expect.`, e, [\"audit\", \"none\"])\n const expect: AgentExpect = {}\n for (const key of [\"audit\", \"none\"] as const) {\n const list = eventPatterns(`${at}.expect.${key}`, e[key])\n if (list) expect[key] = list\n }\n step.expect = expect\n }\n }\n if (issues.length === before) scenario.steps.push(step)\n return\n }\n unknownKeys(`${at}.`, raw, hasCall ? CALL_KEYS : COMMANDS_KEYS)\n if (raw.name !== undefined && typeof raw.name !== \"string\") issues.push(`${at}.name: must be a string`)\n const stepAs = profileName(`${at}.as`, raw.as)\n const common = { ...(typeof raw.name === \"string\" ? { name: raw.name } : {}), ...(stepAs !== undefined ? { as: stepAs } : {}) }\n if (hasCommands) {\n const c = raw.commands\n const list = (key: \"visible\" | \"hidden\"): string[] | undefined => {\n const v = isRecord(c) ? c[key] : undefined\n if (v === undefined) return undefined\n if (!Array.isArray(v) || !v.every((x) => typeof x === \"string\")) {\n issues.push(`${at}.commands.${key}: must be an array of command IDs`)\n return undefined\n }\n return v as string[]\n }\n if (!isRecord(c)) issues.push(`${at}.commands: must be { \"visible\"?: [ids], \"hidden\"?: [ids] }`)\n else {\n unknownKeys(`${at}.commands.`, c, [\"visible\", \"hidden\"])\n if (c.visible === undefined && c.hidden === undefined) issues.push(`${at}.commands: needs \"visible\" or \"hidden\"`)\n }\n const visible = list(\"visible\")\n const hidden = list(\"hidden\")\n if (issues.length === before) {\n scenario.steps.push({ ...common, commands: { ...(visible ? { visible } : {}), ...(hidden ? { hidden } : {}) } })\n }\n return\n }\n const step: CallStep = { ...common, call: \"\" }\n if (typeof raw.call !== \"string\" || raw.call === \"\") issues.push(`${at}.call: must be a command ID`)\n else step.call = raw.call\n if (raw.input !== undefined) {\n if (!isRecord(raw.input)) issues.push(`${at}.input: must be an object`)\n else step.input = raw.input\n }\n for (const key of [\"confirmed\", \"decline\"] as const) {\n if (raw[key] === undefined) continue\n if (typeof raw[key] !== \"boolean\") issues.push(`${at}.${key}: must be true or false`)\n else if (raw[key]) step[key] = true\n }\n if (raw.answers !== undefined) {\n const a = raw.answers\n if (!isRecord(a)) issues.push(`${at}.answers: must be { \"confirm\"?: boolean, \"form\"?: object, \"url\"?: boolean }`)\n else {\n unknownKeys(`${at}.answers.`, a, [\"confirm\", \"form\", \"url\"])\n const answers: Answers = {}\n if (a.confirm !== undefined) {\n if (typeof a.confirm !== \"boolean\") issues.push(`${at}.answers.confirm: must be true or false`)\n else answers.confirm = a.confirm\n }\n if (a.url !== undefined) {\n if (typeof a.url !== \"boolean\") issues.push(`${at}.answers.url: must be true or false`)\n else answers.url = a.url\n }\n if (a.form !== undefined) {\n if (a.form !== false && !isRecord(a.form)) issues.push(`${at}.answers.form: must be an object (the values to send) or false`)\n else answers.form = a.form\n }\n step.answers = answers\n }\n }\n if (raw.expect !== undefined) {\n const e = raw.expect\n if (!isRecord(e)) issues.push(`${at}.expect: must be { \"ok\"?: boolean, \"value\"?: …, \"error\"?: code | { … } }`)\n else {\n unknownKeys(`${at}.expect.`, e, [\"ok\", \"value\", \"error\", \"audit\"])\n const expect: Expect = {}\n const audit = eventPatterns(`${at}.expect.audit`, e.audit)\n if (audit) expect.audit = audit\n if (e.ok !== undefined) {\n if (typeof e.ok !== \"boolean\") issues.push(`${at}.expect.ok: must be true or false`)\n else expect.ok = e.ok\n }\n if (e.value !== undefined) expect.value = e.value\n if (e.error !== undefined) {\n if (typeof e.error !== \"string\" && !isRecord(e.error)) issues.push(`${at}.expect.error: must be an error code or an object`)\n else expect.error = e.error\n }\n if (e.error !== undefined && (e.ok === true || e.value !== undefined)) {\n issues.push(`${at}.expect: \"error\" cannot be combined with \"ok\": true or \"value\"`)\n }\n if (e.ok === false && e.value !== undefined) issues.push(`${at}.expect: \"value\" cannot be combined with \"ok\": false`)\n step.expect = expect\n }\n }\n if (raw.save !== undefined) {\n if (!isRecord(raw.save) || !Object.values(raw.save).every((v) => typeof v === \"string\")) {\n issues.push(`${at}.save: must be { \"<variable>\": \"<path in the result>\" }`)\n } else step.save = raw.save as Record<string, string>\n }\n if (issues.length === before) scenario.steps.push(step)\n })\n }\n if (issues.length > 0) throw new ScenarioFileError(file, issues)\n return scenario\n}\n\nexport interface RunScenarioOptions {\n /** 첫 실패에서 멈춤 */\n bail?: boolean\n signal?: AbortSignal\n onStep?: (result: StepResult) => void\n /** 단계를 시작할 때 (오래 걸리는 agent 단계의 진행 표시) */\n onStepStart?: (info: { step: number; name: string; as: string }) => void\n /** agent 단계가 지시할 에이전트 (§7). 없음 = agent 단계 실패 (AGENT_UNAVAILABLE). close 는 만든 쪽이 한다 */\n agent?: AgentClient\n /** agent 단계: idle 뒤 감사 이벤트를 기다리는 시간 (기본 500ms) */\n agentSettleMs?: number\n}\n\n/** 단계 순서대로 실행. 실패한 단계가 있어도 끝까지 (bail 제외) */\nexport async function runScenario(inspector: Inspector, scenario: Scenario, options: RunScenarioOptions = {}): Promise<ScenarioResult> {\n const started = Date.now()\n const vars = new Map<string, unknown>()\n const steps: StepResult[] = []\n for (const [i, step] of scenario.steps.entries()) {\n if (options.signal?.aborted) break\n const as = \"agent\" in step ? AGENT_AS : inspector.profile(step.as ?? scenario.as).name\n const name = step.name ?? defaultName(step, as)\n options.onStepStart?.({ step: i + 1, name, as })\n const result =\n \"agent\" in step\n ? await runAgent(inspector, step, vars, options)\n : \"call\" in step\n ? await runCall(inspector, step, as, vars, options.signal)\n : await runCommands(inspector, step, as)\n const full: StepResult = { step: i + 1, name, as, ...result, pass: result.failures.length === 0 }\n steps.push(full)\n options.onStep?.(full)\n if (!full.pass && options.bail === true) break\n }\n const result: ScenarioResult = {\n name: scenario.name,\n pass: steps.length === scenario.steps.length && steps.every((s) => s.pass),\n durationMs: Date.now() - started,\n steps,\n }\n if (scenario.file !== undefined) result.file = scenario.file\n return result\n}\n\ntype StepRun = Pick<StepResult, \"durationMs\" | \"outcome\" | \"failures\" | \"record\">\n\nasync function runCall(inspector: Inspector, step: CallStep, as: string, vars: Map<string, unknown>, signal: AbortSignal | undefined): Promise<StepRun> {\n const failures: Failure[] = []\n const missing: string[] = []\n const input = substitute(step.input ?? {}, vars, missing) as Record<string, unknown>\n if (missing.length > 0) {\n for (const name of missing) failures.push({ path: \"input\", expected: `{{${name}}}`, actual: undefined })\n return { durationMs: 0, outcome: undefined, failures }\n }\n const record = await inspector.call(step.call, input, {\n profile: as,\n ...(step.confirmed ? { confirmed: true } : {}),\n ...(step.answers ? { answers: step.answers } : {}),\n ...(step.decline ? { decline: true } : {}),\n ...(signal ? { signal } : {}),\n })\n const outcome = outcomeOf(record)\n compare(step.expect, outcome, failures)\n if (step.expect?.audit) {\n if (!inspector.auditConnected) failures.push({ path: \"audit\", expected: \"an audit source\", actual: AUDIT_UNAVAILABLE })\n else partial(step.expect.audit, await auditOf(inspector, record, step.expect.audit.length, signal), \"audit\", failures)\n }\n for (const [name, path] of Object.entries(step.save ?? {})) {\n const value = outcome.ok ? valueAt(outcome.value, path) : undefined\n if (value === undefined) failures.push({ path: `save.${name}`, expected: path, actual: undefined })\n else vars.set(name, value)\n }\n return { durationMs: record.durationMs, outcome, failures, record: record.id }\n}\n\nconst AUDIT_UNAVAILABLE = \"AUDIT_UNAVAILABLE: audit events are not available for this target (run against the mext dev server of this project, or pass an audit source to createInspector)\"\nconst AGENT_UNAVAILABLE = \"AGENT_UNAVAILABLE: no agent client (use mext inspect run --agent <module>, or runScenario(…, { agent }))\"\n/** 호출의 감사 이벤트가 응답보다 늦게 올 때 기다리는 시간 */\nconst CALL_AUDIT_WAIT_MS = 2_000\n\n/** 이 호출의 감사 이벤트 (trace ID 가 같은 것). 기대한 수만큼 올 때까지 잠깐 기다린다 */\nasync function auditOf(inspector: Inspector, record: CallRecord, count: number, signal: AbortSignal | undefined): Promise<AuditEvent[]> {\n const read = (): AuditEvent[] => inspector.auditEvents().filter((event) => event.traceId === record.traceId)\n const deadline = Date.now() + CALL_AUDIT_WAIT_MS\n while (read().length < count && Date.now() < deadline && signal?.aborted !== true) await new Promise((resolve) => setTimeout(resolve, 20))\n return read()\n}\n\nasync function runAgent(inspector: Inspector, step: AgentStep, vars: Map<string, unknown>, options: RunScenarioOptions): Promise<StepRun> {\n const started = Date.now()\n const failures: Failure[] = []\n const missing: string[] = []\n const text = substitute(step.agent, vars, missing) as string\n for (const name of missing) failures.push({ path: \"agent\", expected: `{{${name}}}`, actual: undefined })\n const agent = options.agent\n if (!agent) failures.push({ path: \"agent\", expected: \"an agent client\", actual: AGENT_UNAVAILABLE })\n if (!inspector.auditConnected) failures.push({ path: \"audit\", expected: \"an audit source\", actual: AUDIT_UNAVAILABLE })\n if (failures.length > 0 || !agent) return { durationMs: 0, outcome: undefined, failures }\n try {\n await connectAgent(agent, options.signal)\n } catch (error) {\n failures.push({ path: \"agent\", expected: \"connected\", actual: error instanceof Error ? error.message : String(error) })\n return { durationMs: Date.now() - started, outcome: undefined, failures }\n }\n const observation = await instructAgent(inspector, agent, {\n text,\n timeoutMs: step.timeoutMs ?? AGENT_TIMEOUT_MS,\n ...(step.from ? { from: step.from } : {}),\n ...(options.agentSettleMs !== undefined ? { settleMs: options.agentSettleMs } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n })\n if (!observation.ok) failures.push({ path: \"agent\", expected: \"idle\", actual: observation.problem })\n checkAudit(step.expect, observation.events, failures)\n return { durationMs: Date.now() - started, outcome: observation, failures }\n}\n\nasync function runCommands(inspector: Inspector, step: CommandsStep, as: string): Promise<StepRun> {\n const started = Date.now()\n const failures: Failure[] = []\n let ids: string[]\n try {\n ids = (await inspector.commands(as)).commands.map((c) => c.id)\n } catch (error) {\n const body = listErrorBody(error)\n failures.push({ path: \"commands\", expected: \"a command list\", actual: body })\n return { durationMs: Date.now() - started, outcome: { ok: false, error: body }, failures }\n }\n const notVisible = (step.commands.visible ?? []).filter((id) => !ids.includes(id))\n const notHidden = (step.commands.hidden ?? []).filter((id) => ids.includes(id))\n if (notVisible.length > 0) failures.push({ path: \"commands.visible\", expected: notVisible, actual: \"hidden\" })\n if (notHidden.length > 0) failures.push({ path: \"commands.hidden\", expected: notHidden, actual: \"visible\" })\n return { durationMs: Date.now() - started, outcome: { ok: true, commands: ids }, failures }\n}\n\n/** 목록 조회 실패 → 오류 본문 */\nexport function listErrorBody(error: unknown): ErrorBody {\n if (error instanceof CallError) return orderedBody(error)\n if (error instanceof ProfileUnavailableError) {\n return { code: error.code, message: error.message, profile: error.profile, missing: [...error.missing] }\n }\n return { code: \"CLIENT_ERROR\", message: error instanceof Error ? error.message : String(error) }\n}\n\nfunction defaultName(step: ScenarioStep, as: string): string {\n if (\"agent\" in step) return `agent: ${step.agent.length > 60 ? `${step.agent.slice(0, 57)}…` : step.agent}`\n return \"call\" in step ? `${step.call} as ${as}` : `commands as ${as}`\n}\n\n/** 기대 결과 비교 (expect 없음 = 통과) */\nexport function compare(expect: Expect | undefined, outcome: CallOutcome, failures: Failure[]): void {\n if (!expect) return\n const wantsError = expect.error !== undefined || expect.ok === false\n const wantsOk = expect.ok === true || expect.value !== undefined\n if (wantsOk && !outcome.ok) {\n failures.push({ path: \"ok\", expected: true, actual: false })\n return\n }\n if (wantsError && outcome.ok) {\n failures.push({ path: \"ok\", expected: false, actual: true })\n return\n }\n if (outcome.ok) {\n if (expect.value !== undefined) partial(expect.value, outcome.value, \"value\", failures)\n return\n }\n if (typeof expect.error === \"string\") {\n if (outcome.error.code !== expect.error) failures.push({ path: \"error.code\", expected: expect.error, actual: outcome.error.code })\n } else if (expect.error !== undefined) partial(expect.error, outcome.error, \"error\", failures)\n}\n\nconst WHOLE = /^\\{\\{\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$/\nconst PART = /\\{\\{\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}/g\n\n/** {{변수}}: 문자열 전체 = 값 그대로 (타입 유지) · 일부 = 문자열로. 없는 변수 = missing 에 이름 */\nfunction substitute(value: unknown, vars: ReadonlyMap<string, unknown>, missing: string[]): unknown {\n if (typeof value === \"string\") {\n const whole = WHOLE.exec(value)\n const lookup = (name: string): unknown => {\n if (!vars.has(name) && !missing.includes(name)) missing.push(name)\n return vars.get(name)\n }\n if (whole) return lookup(whole[1]!)\n return value.replace(PART, (_, name: string) => {\n const v = lookup(name)\n return typeof v === \"string\" ? v : JSON.stringify(v ?? null)\n })\n }\n if (Array.isArray(value)) return value.map((v) => substitute(v, vars, missing))\n if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, substitute(v, vars, missing)]))\n return value\n}\n\n/** 점 구분 경로 (items.0.id) */\nfunction valueAt(value: unknown, path: string): unknown {\n let current = value\n for (const key of path === \"\" ? [] : path.split(\".\")) {\n if (Array.isArray(current)) current = current[Number(key)]\n else if (isRecord(current)) current = current[key]\n else return undefined\n }\n return current\n}\n\n/** 호출 기록 → 시나리오 단계 (관찰한 결과를 기대 결과로). 오류 = code · reason 만 */\nexport function stepFromRecord(record: CallRecord, options: { answers?: Answers; decline?: boolean } = {}): CallStep {\n const step: CallStep = { as: record.profile, call: record.command }\n if (Object.keys(record.input).length > 0) step.input = record.input\n if (record.confirmed) step.confirmed = true\n const answers = options.answers ?? (options.decline ? undefined : answersOf(record))\n if (answers) step.answers = answers\n if (options.decline) step.decline = true\n if (record.ok) step.expect = record.value === undefined ? { ok: true } : { ok: true, value: record.value }\n else {\n const error = record.error\n step.expect = { error: typeof error?.reason === \"string\" ? { code: error.code, reason: error.reason } : (error?.code ?? \"UNKNOWN\") }\n }\n return step\n}\n\n/** 기록의 입력 요청에 보낸 답 → 시나리오 answers (종류별 마지막 답). 답한 요청이 없으면 undefined */\nfunction answersOf(record: CallRecord): Answers | undefined {\n const answers: Answers = {}\n for (const { kind, answer } of record.inputs) {\n if (answer === undefined) continue\n const accepted = answer === true || (typeof answer === \"object\" && answer.action === \"accept\")\n if (kind === \"form\") answers.form = accepted ? ((typeof answer === \"object\" && \"content\" in answer ? answer.content : undefined) ?? {}) : false\n else answers[kind] = accepted\n }\n return Object.keys(answers).length > 0 ? answers : undefined\n}\n\n/** 시나리오 파일 끝에 단계를 덧붙인다 (파일이 없으면 만든다). 반환 = 단계 수 */\nexport function appendStep(file: string, step: ScenarioStep): number {\n let content: Record<string, unknown>\n if (existsSync(file)) {\n loadScenario(file) // 형식 확인\n content = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>\n content.steps = [...(content.steps as unknown[]), step]\n } else {\n mkdirSync(dirname(file), { recursive: true })\n content = { name: basename(file, \".json\"), steps: [step] }\n }\n writeFileSync(file, `${JSON.stringify(content, null, 2)}\\n`)\n return (content.steps as unknown[]).length\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AgentClient } from "./agent.ts";
|
|
2
|
+
import { type Inspector, type InspectTarget } from "./inspector.ts";
|
|
3
|
+
/**
|
|
4
|
+
* 웹 UI 의 중계 서버 (specs/inspector.md §5). 페이지 하나 (ui.html) 와 /api 를 127.0.0.1 에서만 제공한다.
|
|
5
|
+
* 브라우저는 대상 서버를 직접 호출하지 않는다: 호출은 이 프로세스의 Inspector 가 보낸다 (프로필의 토큰은 브라우저로 가지 않는다).
|
|
6
|
+
* 보호 = Host 검사 (DNS rebinding) · Origin 검사 · 페이지에 심은 세션 토큰 헤더
|
|
7
|
+
*/
|
|
8
|
+
export declare const UI_DEFAULT_PORT = 4747;
|
|
9
|
+
/** 세션 토큰 헤더 */
|
|
10
|
+
export declare const UI_TOKEN_HEADER = "x-mext-inspect";
|
|
11
|
+
export interface UiServerOptions {
|
|
12
|
+
inspector: Inspector;
|
|
13
|
+
target: InspectTarget;
|
|
14
|
+
/** 프로젝트 루트 (inspect/scenarios 의 기준) */
|
|
15
|
+
cwd: string;
|
|
16
|
+
/** 기본 4747. 0 = 빈 포트 */
|
|
17
|
+
port?: number;
|
|
18
|
+
/** 감사 이벤트를 받을 수 없는 이유 (Inspector 에 감사 출처가 없을 때 화면에 표시) */
|
|
19
|
+
auditReason?: string;
|
|
20
|
+
/** 시나리오의 agent 단계가 지시할 에이전트 (§7). 없음 = agent 단계 실패 (AGENT_UNAVAILABLE). close 는 만든 쪽이 한다 */
|
|
21
|
+
agent?: AgentClient;
|
|
22
|
+
}
|
|
23
|
+
export interface UiServer {
|
|
24
|
+
/** http://127.0.0.1:<port>/ */
|
|
25
|
+
url: string;
|
|
26
|
+
port: number;
|
|
27
|
+
/** /api 요청의 X-Mext-Inspect 값 (페이지에 심는 값. 테스트용으로 노출) */
|
|
28
|
+
token: string;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export declare function startUiServer(options: UiServerOptions): Promise<UiServer>;
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { CallError } from "../client/typed.js";
|
|
6
|
+
import { VERSION } from "../version-constant.js";
|
|
7
|
+
import { InspectUsageError, orderedBody } from "./inspector.js";
|
|
8
|
+
import { INSPECT_DIR } from "./profiles.js";
|
|
9
|
+
import { appendStep, listErrorBody, loadScenario, runScenario, ScenarioFileError, SCENARIOS_DIR, stepFromRecord } from "./scenario.js";
|
|
10
|
+
/**
|
|
11
|
+
* 웹 UI 의 중계 서버 (specs/inspector.md §5). 페이지 하나 (ui.html) 와 /api 를 127.0.0.1 에서만 제공한다.
|
|
12
|
+
* 브라우저는 대상 서버를 직접 호출하지 않는다: 호출은 이 프로세스의 Inspector 가 보낸다 (프로필의 토큰은 브라우저로 가지 않는다).
|
|
13
|
+
* 보호 = Host 검사 (DNS rebinding) · Origin 검사 · 페이지에 심은 세션 토큰 헤더
|
|
14
|
+
*/
|
|
15
|
+
export const UI_DEFAULT_PORT = 4747;
|
|
16
|
+
/** 세션 토큰 헤더 */
|
|
17
|
+
export const UI_TOKEN_HEADER = "x-mext-inspect";
|
|
18
|
+
const UI_HOST = "127.0.0.1";
|
|
19
|
+
const MAX_BODY = 1024 * 1024;
|
|
20
|
+
const KEEPALIVE_MS = 15_000;
|
|
21
|
+
/** inspect/scenarios 안의 파일 이름만 (경로 구분자 · 상위 폴더 없음) */
|
|
22
|
+
const SCENARIO_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
|
|
23
|
+
class HttpError extends Error {
|
|
24
|
+
status;
|
|
25
|
+
body;
|
|
26
|
+
constructor(status, message, body = {}) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.body = { code: status === 404 ? "NOT_FOUND" : "BAD_REQUEST", message, ...body };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function startUiServer(options) {
|
|
33
|
+
const { inspector } = options;
|
|
34
|
+
const token = randomBytes(24).toString("base64url");
|
|
35
|
+
const expected = Buffer.from(token);
|
|
36
|
+
const page = readFileSync(new URL("./ui.html", import.meta.url), "utf8");
|
|
37
|
+
const scenariosDir = join(resolve(options.cwd), INSPECT_DIR, SCENARIOS_DIR);
|
|
38
|
+
const calls = new Map();
|
|
39
|
+
const streams = new Set();
|
|
40
|
+
let callSeq = 0;
|
|
41
|
+
let port = 0;
|
|
42
|
+
const authorized = (req) => {
|
|
43
|
+
const header = req.headers[UI_TOKEN_HEADER];
|
|
44
|
+
const given = Buffer.from(typeof header === "string" ? header : "");
|
|
45
|
+
return given.length === expected.length && timingSafeEqual(given, expected);
|
|
46
|
+
};
|
|
47
|
+
/** Host · Origin = 이 서버의 주소만 */
|
|
48
|
+
const sameSite = (req) => {
|
|
49
|
+
const hosts = [`${UI_HOST}:${port}`, `localhost:${port}`];
|
|
50
|
+
if (!hosts.includes(req.headers.host ?? ""))
|
|
51
|
+
return false;
|
|
52
|
+
const origin = req.headers.origin;
|
|
53
|
+
return origin === undefined || hosts.some((host) => origin === `http://${host}`);
|
|
54
|
+
};
|
|
55
|
+
const scenarioPath = (name) => {
|
|
56
|
+
if (typeof name !== "string" || !SCENARIO_NAME.test(name)) {
|
|
57
|
+
throw new HttpError(400, `file must be a scenario file name inside ${INSPECT_DIR}/${SCENARIOS_DIR} (<name>.json)`);
|
|
58
|
+
}
|
|
59
|
+
return join(scenariosDir, name);
|
|
60
|
+
};
|
|
61
|
+
const profileNames = (value) => {
|
|
62
|
+
const names = (value ?? "")
|
|
63
|
+
.split(",")
|
|
64
|
+
.map((s) => s.trim())
|
|
65
|
+
.filter((s) => s !== "");
|
|
66
|
+
return (names.length > 0 ? names : [inspector.profileSet.default]).map((name) => inspector.profile(name).name);
|
|
67
|
+
};
|
|
68
|
+
const routes = {
|
|
69
|
+
"GET /api/state": async (_req, res) => {
|
|
70
|
+
let server;
|
|
71
|
+
try {
|
|
72
|
+
server = await inspector.server();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
server = { error: listErrorBody(error) };
|
|
76
|
+
}
|
|
77
|
+
json(res, 200, {
|
|
78
|
+
version: VERSION,
|
|
79
|
+
target: options.target,
|
|
80
|
+
server,
|
|
81
|
+
default: inspector.profileSet.default,
|
|
82
|
+
profiles: inspector.profiles(),
|
|
83
|
+
audit: inspector.auditConnected ? { available: true } : { available: false, reason: options.auditReason ?? "No audit source is connected." },
|
|
84
|
+
agent: options.agent ? { available: true, ...(options.agent.name !== undefined ? { name: options.agent.name } : {}) } : { available: false },
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
"GET /api/commands": async (_req, res, url) => {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const profile of profileNames(url.searchParams.get("as"))) {
|
|
90
|
+
try {
|
|
91
|
+
out.push({ profile, ok: true, list: await inspector.commands(profile) });
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
out.push({ profile, ok: false, error: listErrorBody(error) });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
json(res, 200, out);
|
|
98
|
+
},
|
|
99
|
+
"GET /api/command": async (_req, res, url) => {
|
|
100
|
+
const id = url.searchParams.get("id") ?? "";
|
|
101
|
+
let info;
|
|
102
|
+
let firstError;
|
|
103
|
+
const visibleTo = {};
|
|
104
|
+
for (const profile of profileNames(url.searchParams.get("as"))) {
|
|
105
|
+
try {
|
|
106
|
+
const found = await inspector.describe(id, profile);
|
|
107
|
+
info ??= found;
|
|
108
|
+
visibleTo[profile] = true;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
firstError ??= listErrorBody(error);
|
|
112
|
+
visibleTo[profile] = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!info)
|
|
116
|
+
return json(res, 404, { error: firstError, visibleTo });
|
|
117
|
+
json(res, 200, { info, visibleTo });
|
|
118
|
+
},
|
|
119
|
+
"POST /api/call": async (req, res) => {
|
|
120
|
+
const body = await readJson(req);
|
|
121
|
+
if (typeof body.id !== "string" || body.id === "")
|
|
122
|
+
throw new HttpError(400, "id must be a command ID");
|
|
123
|
+
if (body.input !== undefined && !isRecord(body.input))
|
|
124
|
+
throw new HttpError(400, "input must be an object");
|
|
125
|
+
const profile = inspector.profile(typeof body.as === "string" ? body.as : undefined).name;
|
|
126
|
+
const call = String(++callSeq);
|
|
127
|
+
const active = { controller: new AbortController(), inputs: new Map() };
|
|
128
|
+
calls.set(call, active);
|
|
129
|
+
let finished = false;
|
|
130
|
+
let requestSeq = 0;
|
|
131
|
+
const send = ndjson(res);
|
|
132
|
+
// 브라우저가 스트림을 닫음 = 취소
|
|
133
|
+
res.on("close", () => {
|
|
134
|
+
if (finished)
|
|
135
|
+
return;
|
|
136
|
+
active.controller.abort(new Error("Cancelled."));
|
|
137
|
+
for (const pending of active.inputs.values())
|
|
138
|
+
pending.reject(new Error("Cancelled."));
|
|
139
|
+
active.inputs.clear();
|
|
140
|
+
});
|
|
141
|
+
send({ type: "start", call });
|
|
142
|
+
try {
|
|
143
|
+
const record = await inspector.call(body.id, body.input ?? {}, {
|
|
144
|
+
profile,
|
|
145
|
+
...(body.confirmed === true ? { confirmed: true } : {}),
|
|
146
|
+
signal: active.controller.signal,
|
|
147
|
+
onProgress: (update) => send({ type: "progress", update }),
|
|
148
|
+
onTask: (task) => send({ type: "task", task }),
|
|
149
|
+
onInput: (request) => new Promise((resolveAnswer, reject) => {
|
|
150
|
+
const id = String(++requestSeq);
|
|
151
|
+
active.inputs.set(id, { resolve: resolveAnswer, reject });
|
|
152
|
+
send({
|
|
153
|
+
type: "input",
|
|
154
|
+
request: {
|
|
155
|
+
id,
|
|
156
|
+
kind: request.kind,
|
|
157
|
+
message: request.message,
|
|
158
|
+
...(request.schema ? { schema: request.schema } : {}),
|
|
159
|
+
...(request.url !== undefined ? { url: request.url } : {}),
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
send({ type: "record", record });
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
finished = true;
|
|
168
|
+
calls.delete(call);
|
|
169
|
+
res.end();
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"POST /api/call/input": async (req, res) => {
|
|
173
|
+
const body = await readJson(req);
|
|
174
|
+
const pending = calls.get(String(body.call))?.inputs.get(String(body.request));
|
|
175
|
+
if (!pending)
|
|
176
|
+
throw new HttpError(404, "No input request is waiting for this answer.");
|
|
177
|
+
const answer = body.answer;
|
|
178
|
+
const valid = typeof answer === "boolean" ||
|
|
179
|
+
(isRecord(answer) &&
|
|
180
|
+
(answer.action === "decline" || answer.action === "cancel" || (answer.action === "accept" && (answer.content === undefined || isRecord(answer.content)))));
|
|
181
|
+
if (!valid)
|
|
182
|
+
throw new HttpError(400, 'answer must be true, false, or { "action": "accept" | "decline" | "cancel", "content"?: object }');
|
|
183
|
+
calls.get(String(body.call)).inputs.delete(String(body.request));
|
|
184
|
+
pending.resolve(answer);
|
|
185
|
+
json(res, 200, { ok: true });
|
|
186
|
+
},
|
|
187
|
+
"GET /api/history": (_req, res) => json(res, 200, inspector.history()),
|
|
188
|
+
"DELETE /api/history": (_req, res) => {
|
|
189
|
+
inspector.clearHistory();
|
|
190
|
+
json(res, 200, { ok: true });
|
|
191
|
+
},
|
|
192
|
+
"GET /api/audit": (_req, res) => json(res, 200, inspector.auditEvents()),
|
|
193
|
+
"GET /api/scenarios": (_req, res) => {
|
|
194
|
+
const names = existsSync(scenariosDir)
|
|
195
|
+
? readdirSync(scenariosDir)
|
|
196
|
+
.filter((name) => SCENARIO_NAME.test(name))
|
|
197
|
+
.sort()
|
|
198
|
+
: [];
|
|
199
|
+
json(res, 200, names.map((file) => {
|
|
200
|
+
try {
|
|
201
|
+
const scenario = loadScenario(join(scenariosDir, file), inspector.profileSet);
|
|
202
|
+
return {
|
|
203
|
+
file,
|
|
204
|
+
name: scenario.name,
|
|
205
|
+
...(scenario.description !== undefined ? { description: scenario.description } : {}),
|
|
206
|
+
steps: scenario.steps.length,
|
|
207
|
+
agentSteps: scenario.steps.filter((step) => "agent" in step).length,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return { file, name: file, steps: 0, error: error instanceof ScenarioFileError ? [...error.issues] : [String(error)] };
|
|
212
|
+
}
|
|
213
|
+
}));
|
|
214
|
+
},
|
|
215
|
+
"POST /api/scenarios/run": async (req, res) => {
|
|
216
|
+
const body = await readJson(req);
|
|
217
|
+
const file = scenarioPath(body.file);
|
|
218
|
+
if (!existsSync(file))
|
|
219
|
+
throw new HttpError(404, `No scenario ${String(body.file)}.`);
|
|
220
|
+
let scenario;
|
|
221
|
+
try {
|
|
222
|
+
scenario = loadScenario(file, inspector.profileSet);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof ScenarioFileError)
|
|
226
|
+
throw new HttpError(400, "Invalid scenario file.", { issues: [...error.issues] });
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
const controller = new AbortController();
|
|
230
|
+
let finished = false;
|
|
231
|
+
res.on("close", () => {
|
|
232
|
+
if (!finished)
|
|
233
|
+
controller.abort(new Error("Cancelled."));
|
|
234
|
+
});
|
|
235
|
+
const send = ndjson(res);
|
|
236
|
+
try {
|
|
237
|
+
const result = await runScenario(inspector, scenario, {
|
|
238
|
+
bail: body.bail === true,
|
|
239
|
+
signal: controller.signal,
|
|
240
|
+
...(options.agent ? { agent: options.agent } : {}),
|
|
241
|
+
onStepStart: (info) => send({ type: "step-start", ...info }),
|
|
242
|
+
onStep: (step) => send({ type: "step", step }),
|
|
243
|
+
});
|
|
244
|
+
send({ type: "result", result: { ...result, file: body.file } });
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
finished = true;
|
|
248
|
+
res.end();
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"POST /api/scenarios/append": async (req, res) => {
|
|
252
|
+
const body = await readJson(req);
|
|
253
|
+
const file = scenarioPath(body.file);
|
|
254
|
+
const record = inspector.history().find((r) => r.id === body.record);
|
|
255
|
+
if (!record)
|
|
256
|
+
throw new HttpError(404, "No such call in the history.");
|
|
257
|
+
try {
|
|
258
|
+
json(res, 200, { file: body.file, steps: appendStep(file, stepFromRecord(record)) });
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (error instanceof ScenarioFileError)
|
|
262
|
+
throw new HttpError(400, "The existing scenario file is invalid.", { issues: [...error.issues] });
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"GET /api/events": (_req, res) => {
|
|
267
|
+
const send = ndjson(res);
|
|
268
|
+
const unsubscribe = inspector.subscribe((event) => send(event));
|
|
269
|
+
streams.add(res);
|
|
270
|
+
res.on("close", () => {
|
|
271
|
+
unsubscribe();
|
|
272
|
+
streams.delete(res);
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
const handle = async (req, res) => {
|
|
277
|
+
res.setHeader("cache-control", "no-store");
|
|
278
|
+
res.setHeader("x-content-type-options", "nosniff");
|
|
279
|
+
if (!sameSite(req))
|
|
280
|
+
return json(res, 403, { code: "FORBIDDEN", message: "Forbidden." });
|
|
281
|
+
const url = new URL(req.url ?? "/", `http://${UI_HOST}:${port}`);
|
|
282
|
+
if (url.pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
|
|
283
|
+
const nonce = randomBytes(16).toString("base64");
|
|
284
|
+
res.writeHead(200, {
|
|
285
|
+
"content-type": "text/html; charset=utf-8",
|
|
286
|
+
"content-security-policy": `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'`,
|
|
287
|
+
"referrer-policy": "no-referrer",
|
|
288
|
+
});
|
|
289
|
+
res.end(req.method === "HEAD" ? undefined : page.replaceAll("__MEXT_INSPECT_NONCE__", nonce).replaceAll("__MEXT_INSPECT_TOKEN__", token));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (!url.pathname.startsWith("/api/"))
|
|
293
|
+
return json(res, 404, { code: "NOT_FOUND", message: "Not found." });
|
|
294
|
+
if (!authorized(req))
|
|
295
|
+
return json(res, 403, { code: "FORBIDDEN", message: "Forbidden." });
|
|
296
|
+
const route = routes[`${req.method} ${url.pathname}`];
|
|
297
|
+
if (!route)
|
|
298
|
+
return json(res, 404, { code: "NOT_FOUND", message: "Not found." });
|
|
299
|
+
await route(req, res, url);
|
|
300
|
+
};
|
|
301
|
+
const server = createServer((req, res) => {
|
|
302
|
+
handle(req, res).catch((error) => {
|
|
303
|
+
const [status, body] = error instanceof HttpError
|
|
304
|
+
? [error.status, error.body]
|
|
305
|
+
: error instanceof CallError
|
|
306
|
+
? [502, orderedBody(error)]
|
|
307
|
+
: [error instanceof InspectUsageError ? 400 : 500, { code: "INSPECT_ERROR", message: error instanceof Error ? error.message : String(error) }];
|
|
308
|
+
if (res.headersSent)
|
|
309
|
+
res.end();
|
|
310
|
+
else
|
|
311
|
+
json(res, status, body);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
await new Promise((done, reject) => {
|
|
315
|
+
server.once("error", reject);
|
|
316
|
+
server.listen(options.port ?? UI_DEFAULT_PORT, UI_HOST, () => {
|
|
317
|
+
server.off("error", reject);
|
|
318
|
+
done();
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
port = server.address().port;
|
|
322
|
+
const keepalive = setInterval(() => {
|
|
323
|
+
for (const res of streams)
|
|
324
|
+
res.write("\n");
|
|
325
|
+
}, KEEPALIVE_MS);
|
|
326
|
+
keepalive.unref();
|
|
327
|
+
return {
|
|
328
|
+
url: `http://${UI_HOST}:${port}/`,
|
|
329
|
+
port,
|
|
330
|
+
token,
|
|
331
|
+
async close() {
|
|
332
|
+
clearInterval(keepalive);
|
|
333
|
+
for (const active of calls.values())
|
|
334
|
+
active.controller.abort(new Error("Cancelled."));
|
|
335
|
+
server.closeAllConnections();
|
|
336
|
+
await new Promise((done) => server.close(() => done()));
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function json(res, status, body) {
|
|
341
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
342
|
+
res.end(JSON.stringify(body));
|
|
343
|
+
}
|
|
344
|
+
/** NDJSON 스트림 시작. 반환 = 한 줄 쓰기 (닫힌 뒤에는 무시) */
|
|
345
|
+
function ndjson(res) {
|
|
346
|
+
res.writeHead(200, { "content-type": "application/x-ndjson; charset=utf-8", "x-accel-buffering": "no" });
|
|
347
|
+
res.flushHeaders();
|
|
348
|
+
return (value) => {
|
|
349
|
+
if (!res.writableEnded && !res.destroyed)
|
|
350
|
+
res.write(`${JSON.stringify(value)}\n`);
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
async function readJson(req) {
|
|
354
|
+
if (!(req.headers["content-type"] ?? "").startsWith("application/json"))
|
|
355
|
+
throw new HttpError(400, "Content-Type must be application/json");
|
|
356
|
+
const chunks = [];
|
|
357
|
+
let size = 0;
|
|
358
|
+
for await (const chunk of req) {
|
|
359
|
+
size += chunk.length;
|
|
360
|
+
if (size > MAX_BODY)
|
|
361
|
+
throw new HttpError(400, "Request body too large.");
|
|
362
|
+
chunks.push(chunk);
|
|
363
|
+
}
|
|
364
|
+
let value;
|
|
365
|
+
try {
|
|
366
|
+
value = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
throw new HttpError(400, "Request body must be JSON.");
|
|
370
|
+
}
|
|
371
|
+
if (!isRecord(value))
|
|
372
|
+
throw new HttpError(400, "Request body must be a JSON object.");
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
function isRecord(value) {
|
|
376
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
377
|
+
}
|
|
378
|
+
//# sourceMappingURL=ui-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-server.js","sourceRoot":"","sources":["../../src/inspect/ui-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes, timingSafeEqual } from \"node:crypto\"\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\"\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\"\nimport type { AddressInfo } from \"node:net\"\nimport { join, resolve } from \"node:path\"\nimport { CallError } from \"../client/typed.ts\"\nimport type { CommandInfo, CommandList, InputAnswer, InputRequestInfo } from \"../client/types.ts\"\nimport { VERSION } from \"../version-constant.ts\"\nimport type { AgentClient } from \"./agent.ts\"\nimport { InspectUsageError, orderedBody, type ErrorBody, type Inspector, type InspectTarget } from \"./inspector.ts\"\nimport { INSPECT_DIR } from \"./profiles.ts\"\nimport { appendStep, listErrorBody, loadScenario, runScenario, ScenarioFileError, SCENARIOS_DIR, stepFromRecord } from \"./scenario.ts\"\n\n/**\n * 웹 UI 의 중계 서버 (specs/inspector.md §5). 페이지 하나 (ui.html) 와 /api 를 127.0.0.1 에서만 제공한다.\n * 브라우저는 대상 서버를 직접 호출하지 않는다: 호출은 이 프로세스의 Inspector 가 보낸다 (프로필의 토큰은 브라우저로 가지 않는다).\n * 보호 = Host 검사 (DNS rebinding) · Origin 검사 · 페이지에 심은 세션 토큰 헤더\n */\n\nexport const UI_DEFAULT_PORT = 4747\n/** 세션 토큰 헤더 */\nexport const UI_TOKEN_HEADER = \"x-mext-inspect\"\nconst UI_HOST = \"127.0.0.1\"\nconst MAX_BODY = 1024 * 1024\nconst KEEPALIVE_MS = 15_000\n/** inspect/scenarios 안의 파일 이름만 (경로 구분자 · 상위 폴더 없음) */\nconst SCENARIO_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*\\.json$/\n\nexport interface UiServerOptions {\n inspector: Inspector\n target: InspectTarget\n /** 프로젝트 루트 (inspect/scenarios 의 기준) */\n cwd: string\n /** 기본 4747. 0 = 빈 포트 */\n port?: number\n /** 감사 이벤트를 받을 수 없는 이유 (Inspector 에 감사 출처가 없을 때 화면에 표시) */\n auditReason?: string\n /** 시나리오의 agent 단계가 지시할 에이전트 (§7). 없음 = agent 단계 실패 (AGENT_UNAVAILABLE). close 는 만든 쪽이 한다 */\n agent?: AgentClient\n}\n\nexport interface UiServer {\n /** http://127.0.0.1:<port>/ */\n url: string\n port: number\n /** /api 요청의 X-Mext-Inspect 값 (페이지에 심는 값. 테스트용으로 노출) */\n token: string\n close(): Promise<void>\n}\n\nclass HttpError extends Error {\n readonly status: number\n readonly body: Record<string, unknown>\n constructor(status: number, message: string, body: Record<string, unknown> = {}) {\n super(message)\n this.status = status\n this.body = { code: status === 404 ? \"NOT_FOUND\" : \"BAD_REQUEST\", message, ...body }\n }\n}\n\ninterface ActiveCall {\n controller: AbortController\n /** 대기 중인 입력 요청 → 답 */\n inputs: Map<string, { resolve: (answer: InputAnswer) => void; reject: (error: Error) => void }>\n}\n\nexport async function startUiServer(options: UiServerOptions): Promise<UiServer> {\n const { inspector } = options\n const token = randomBytes(24).toString(\"base64url\")\n const expected = Buffer.from(token)\n const page = readFileSync(new URL(\"./ui.html\", import.meta.url), \"utf8\")\n const scenariosDir = join(resolve(options.cwd), INSPECT_DIR, SCENARIOS_DIR)\n const calls = new Map<string, ActiveCall>()\n const streams = new Set<ServerResponse>()\n let callSeq = 0\n let port = 0\n\n const authorized = (req: IncomingMessage): boolean => {\n const header = req.headers[UI_TOKEN_HEADER]\n const given = Buffer.from(typeof header === \"string\" ? header : \"\")\n return given.length === expected.length && timingSafeEqual(given, expected)\n }\n /** Host · Origin = 이 서버의 주소만 */\n const sameSite = (req: IncomingMessage): boolean => {\n const hosts = [`${UI_HOST}:${port}`, `localhost:${port}`]\n if (!hosts.includes(req.headers.host ?? \"\")) return false\n const origin = req.headers.origin\n return origin === undefined || hosts.some((host) => origin === `http://${host}`)\n }\n\n const scenarioPath = (name: unknown): string => {\n if (typeof name !== \"string\" || !SCENARIO_NAME.test(name)) {\n throw new HttpError(400, `file must be a scenario file name inside ${INSPECT_DIR}/${SCENARIOS_DIR} (<name>.json)`)\n }\n return join(scenariosDir, name)\n }\n\n const profileNames = (value: string | null): string[] => {\n const names = (value ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s !== \"\")\n return (names.length > 0 ? names : [inspector.profileSet.default]).map((name) => inspector.profile(name).name)\n }\n\n const routes: Record<string, (req: IncomingMessage, res: ServerResponse, url: URL) => Promise<void> | void> = {\n \"GET /api/state\": async (_req, res) => {\n let server: unknown\n try {\n server = await inspector.server()\n } catch (error) {\n server = { error: listErrorBody(error) }\n }\n json(res, 200, {\n version: VERSION,\n target: options.target,\n server,\n default: inspector.profileSet.default,\n profiles: inspector.profiles(),\n audit: inspector.auditConnected ? { available: true } : { available: false, reason: options.auditReason ?? \"No audit source is connected.\" },\n agent: options.agent ? { available: true, ...(options.agent.name !== undefined ? { name: options.agent.name } : {}) } : { available: false },\n })\n },\n \"GET /api/commands\": async (_req, res, url) => {\n const out: ({ profile: string; ok: true; list: CommandList } | { profile: string; ok: false; error: ErrorBody })[] = []\n for (const profile of profileNames(url.searchParams.get(\"as\"))) {\n try {\n out.push({ profile, ok: true, list: await inspector.commands(profile) })\n } catch (error) {\n out.push({ profile, ok: false, error: listErrorBody(error) })\n }\n }\n json(res, 200, out)\n },\n \"GET /api/command\": async (_req, res, url) => {\n const id = url.searchParams.get(\"id\") ?? \"\"\n let info: CommandInfo | undefined\n let firstError: ErrorBody | undefined\n const visibleTo: Record<string, boolean> = {}\n for (const profile of profileNames(url.searchParams.get(\"as\"))) {\n try {\n const found = await inspector.describe(id, profile)\n info ??= found\n visibleTo[profile] = true\n } catch (error) {\n firstError ??= listErrorBody(error)\n visibleTo[profile] = false\n }\n }\n if (!info) return json(res, 404, { error: firstError, visibleTo })\n json(res, 200, { info, visibleTo })\n },\n \"POST /api/call\": async (req, res) => {\n const body = await readJson(req)\n if (typeof body.id !== \"string\" || body.id === \"\") throw new HttpError(400, \"id must be a command ID\")\n if (body.input !== undefined && !isRecord(body.input)) throw new HttpError(400, \"input must be an object\")\n const profile = inspector.profile(typeof body.as === \"string\" ? body.as : undefined).name\n const call = String(++callSeq)\n const active: ActiveCall = { controller: new AbortController(), inputs: new Map() }\n calls.set(call, active)\n let finished = false\n let requestSeq = 0\n const send = ndjson(res)\n // 브라우저가 스트림을 닫음 = 취소\n res.on(\"close\", () => {\n if (finished) return\n active.controller.abort(new Error(\"Cancelled.\"))\n for (const pending of active.inputs.values()) pending.reject(new Error(\"Cancelled.\"))\n active.inputs.clear()\n })\n send({ type: \"start\", call })\n try {\n const record = await inspector.call(body.id, body.input ?? {}, {\n profile,\n ...(body.confirmed === true ? { confirmed: true } : {}),\n signal: active.controller.signal,\n onProgress: (update) => send({ type: \"progress\", update }),\n onTask: (task) => send({ type: \"task\", task }),\n onInput: (request: InputRequestInfo) =>\n new Promise<InputAnswer>((resolveAnswer, reject) => {\n const id = String(++requestSeq)\n active.inputs.set(id, { resolve: resolveAnswer, reject })\n send({\n type: \"input\",\n request: {\n id,\n kind: request.kind,\n message: request.message,\n ...(request.schema ? { schema: request.schema } : {}),\n ...(request.url !== undefined ? { url: request.url } : {}),\n },\n })\n }),\n })\n send({ type: \"record\", record })\n } finally {\n finished = true\n calls.delete(call)\n res.end()\n }\n },\n \"POST /api/call/input\": async (req, res) => {\n const body = await readJson(req)\n const pending = calls.get(String(body.call))?.inputs.get(String(body.request))\n if (!pending) throw new HttpError(404, \"No input request is waiting for this answer.\")\n const answer = body.answer\n const valid =\n typeof answer === \"boolean\" ||\n (isRecord(answer) &&\n (answer.action === \"decline\" || answer.action === \"cancel\" || (answer.action === \"accept\" && (answer.content === undefined || isRecord(answer.content)))))\n if (!valid) throw new HttpError(400, 'answer must be true, false, or { \"action\": \"accept\" | \"decline\" | \"cancel\", \"content\"?: object }')\n calls.get(String(body.call))!.inputs.delete(String(body.request))\n pending.resolve(answer as InputAnswer)\n json(res, 200, { ok: true })\n },\n \"GET /api/history\": (_req, res) => json(res, 200, inspector.history()),\n \"DELETE /api/history\": (_req, res) => {\n inspector.clearHistory()\n json(res, 200, { ok: true })\n },\n \"GET /api/audit\": (_req, res) => json(res, 200, inspector.auditEvents()),\n \"GET /api/scenarios\": (_req, res) => {\n const names = existsSync(scenariosDir)\n ? readdirSync(scenariosDir)\n .filter((name) => SCENARIO_NAME.test(name))\n .sort()\n : []\n json(\n res,\n 200,\n names.map((file) => {\n try {\n const scenario = loadScenario(join(scenariosDir, file), inspector.profileSet)\n return {\n file,\n name: scenario.name,\n ...(scenario.description !== undefined ? { description: scenario.description } : {}),\n steps: scenario.steps.length,\n agentSteps: scenario.steps.filter((step) => \"agent\" in step).length,\n }\n } catch (error) {\n return { file, name: file, steps: 0, error: error instanceof ScenarioFileError ? [...error.issues] : [String(error)] }\n }\n }),\n )\n },\n \"POST /api/scenarios/run\": async (req, res) => {\n const body = await readJson(req)\n const file = scenarioPath(body.file)\n if (!existsSync(file)) throw new HttpError(404, `No scenario ${String(body.file)}.`)\n let scenario\n try {\n scenario = loadScenario(file, inspector.profileSet)\n } catch (error) {\n if (error instanceof ScenarioFileError) throw new HttpError(400, \"Invalid scenario file.\", { issues: [...error.issues] })\n throw error\n }\n const controller = new AbortController()\n let finished = false\n res.on(\"close\", () => {\n if (!finished) controller.abort(new Error(\"Cancelled.\"))\n })\n const send = ndjson(res)\n try {\n const result = await runScenario(inspector, scenario, {\n bail: body.bail === true,\n signal: controller.signal,\n ...(options.agent ? { agent: options.agent } : {}),\n onStepStart: (info) => send({ type: \"step-start\", ...info }),\n onStep: (step) => send({ type: \"step\", step }),\n })\n send({ type: \"result\", result: { ...result, file: body.file } })\n } finally {\n finished = true\n res.end()\n }\n },\n \"POST /api/scenarios/append\": async (req, res) => {\n const body = await readJson(req)\n const file = scenarioPath(body.file)\n const record = inspector.history().find((r) => r.id === body.record)\n if (!record) throw new HttpError(404, \"No such call in the history.\")\n try {\n json(res, 200, { file: body.file, steps: appendStep(file, stepFromRecord(record)) })\n } catch (error) {\n if (error instanceof ScenarioFileError) throw new HttpError(400, \"The existing scenario file is invalid.\", { issues: [...error.issues] })\n throw error\n }\n },\n \"GET /api/events\": (_req, res) => {\n const send = ndjson(res)\n const unsubscribe = inspector.subscribe((event) => send(event))\n streams.add(res)\n res.on(\"close\", () => {\n unsubscribe()\n streams.delete(res)\n })\n },\n }\n\n const handle = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n res.setHeader(\"cache-control\", \"no-store\")\n res.setHeader(\"x-content-type-options\", \"nosniff\")\n if (!sameSite(req)) return json(res, 403, { code: \"FORBIDDEN\", message: \"Forbidden.\" })\n const url = new URL(req.url ?? \"/\", `http://${UI_HOST}:${port}`)\n if (url.pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n const nonce = randomBytes(16).toString(\"base64\")\n res.writeHead(200, {\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-security-policy\": `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'`,\n \"referrer-policy\": \"no-referrer\",\n })\n res.end(req.method === \"HEAD\" ? undefined : page.replaceAll(\"__MEXT_INSPECT_NONCE__\", nonce).replaceAll(\"__MEXT_INSPECT_TOKEN__\", token))\n return\n }\n if (!url.pathname.startsWith(\"/api/\")) return json(res, 404, { code: \"NOT_FOUND\", message: \"Not found.\" })\n if (!authorized(req)) return json(res, 403, { code: \"FORBIDDEN\", message: \"Forbidden.\" })\n const route = routes[`${req.method} ${url.pathname}`]\n if (!route) return json(res, 404, { code: \"NOT_FOUND\", message: \"Not found.\" })\n await route(req, res, url)\n }\n\n const server: Server = createServer((req, res) => {\n handle(req, res).catch((error: unknown) => {\n const [status, body] =\n error instanceof HttpError\n ? [error.status, error.body]\n : error instanceof CallError\n ? [502, orderedBody(error)]\n : [error instanceof InspectUsageError ? 400 : 500, { code: \"INSPECT_ERROR\", message: error instanceof Error ? error.message : String(error) }]\n if (res.headersSent) res.end()\n else json(res, status, body)\n })\n })\n await new Promise<void>((done, reject) => {\n server.once(\"error\", reject)\n server.listen(options.port ?? UI_DEFAULT_PORT, UI_HOST, () => {\n server.off(\"error\", reject)\n done()\n })\n })\n port = (server.address() as AddressInfo).port\n const keepalive = setInterval(() => {\n for (const res of streams) res.write(\"\\n\")\n }, KEEPALIVE_MS)\n keepalive.unref()\n\n return {\n url: `http://${UI_HOST}:${port}/`,\n port,\n token,\n async close() {\n clearInterval(keepalive)\n for (const active of calls.values()) active.controller.abort(new Error(\"Cancelled.\"))\n server.closeAllConnections()\n await new Promise<void>((done) => server.close(() => done()))\n },\n }\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { \"content-type\": \"application/json; charset=utf-8\" })\n res.end(JSON.stringify(body))\n}\n\n/** NDJSON 스트림 시작. 반환 = 한 줄 쓰기 (닫힌 뒤에는 무시) */\nfunction ndjson(res: ServerResponse): (value: unknown) => void {\n res.writeHead(200, { \"content-type\": \"application/x-ndjson; charset=utf-8\", \"x-accel-buffering\": \"no\" })\n res.flushHeaders()\n return (value) => {\n if (!res.writableEnded && !res.destroyed) res.write(`${JSON.stringify(value)}\\n`)\n }\n}\n\nasync function readJson(req: IncomingMessage): Promise<Record<string, unknown>> {\n if (!(req.headers[\"content-type\"] ?? \"\").startsWith(\"application/json\")) throw new HttpError(400, \"Content-Type must be application/json\")\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n size += (chunk as Buffer).length\n if (size > MAX_BODY) throw new HttpError(400, \"Request body too large.\")\n chunks.push(chunk as Buffer)\n }\n let value: unknown\n try {\n value = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"))\n } catch {\n throw new HttpError(400, \"Request body must be JSON.\")\n }\n if (!isRecord(value)) throw new HttpError(400, \"Request body must be a JSON object.\")\n return value\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n"]}
|