@ainecto/cli 0.8.0 → 0.9.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/bin/mcp.js CHANGED
@@ -179,6 +179,15 @@ var OAuthLoginError = class extends Error {
179
179
  }
180
180
  code;
181
181
  };
182
+ var OAuthReauthRequiredError = class extends Error {
183
+ constructor(oauthError, status) {
184
+ super(`The stored sign-in was rejected by the server (${oauthError}, HTTP ${status}). Sign in again.`);
185
+ this.oauthError = oauthError;
186
+ this.name = "OAuthReauthRequiredError";
187
+ }
188
+ oauthError;
189
+ };
190
+ var REAUTH_REQUIRED_ERRORS = /* @__PURE__ */ new Set(["invalid_grant", "invalid_client", "unauthorized_client"]);
182
191
  var OAuthClient = class {
183
192
  constructor(options) {
184
193
  this.options = options;
@@ -374,10 +383,22 @@ async function exchangeToken(tokenEndpoint, params, fetchImpl) {
374
383
  body: new URLSearchParams(params)
375
384
  });
376
385
  if (!response.ok) {
377
- throw new Error(`OAuth token exchange failed with HTTP ${response.status}.`);
386
+ const oauthError = await readOAuthErrorCode(response);
387
+ if (params.grant_type === "refresh_token" && oauthError !== void 0 && REAUTH_REQUIRED_ERRORS.has(oauthError)) {
388
+ throw new OAuthReauthRequiredError(oauthError, response.status);
389
+ }
390
+ throw new Error(`OAuth token exchange failed with HTTP ${response.status}${oauthError ? ` (${oauthError})` : ""}.`);
378
391
  }
379
392
  return response.json();
380
393
  }
394
+ async function readOAuthErrorCode(response) {
395
+ try {
396
+ const body = await response.json();
397
+ return typeof body.error === "string" ? body.error : void 0;
398
+ } catch {
399
+ return void 0;
400
+ }
401
+ }
381
402
  function toStoredToken(endpoint, token, metadata, previous) {
382
403
  if (typeof token.access_token !== "string") {
383
404
  throw new Error("OAuth token response did not include access_token.");
@@ -526,7 +547,7 @@ function legacyRoleInput(flag, envVars = process.env) {
526
547
  }
527
548
 
528
549
  // src/core/version.ts
529
- var CLI_VERSION = true ? "0.8.0" : UNBUNDLED_VERSION;
550
+ var CLI_VERSION = true ? "0.9.0" : UNBUNDLED_VERSION;
530
551
  var PACKAGE_NAME = "@ainecto/mcp";
531
552
  var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
532
553
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/config/endpoints.ts","../../src/core/auth/tokenStore.ts","../../src/core/auth/oauth.ts","../../src/core/auth/clientRegistration.ts","../../src/core/harness/legacyRole.ts","../../src/core/version.ts","../../src/core/mcp/rpcClient.ts","../../src/core/mcp/stdioBridge.ts","../../src/adapters/mcp/connector.ts","../../src/core/output/render.ts","../../src/adapters/mcp/args.ts","../../src/bin/mcp.ts"],"sourcesContent":["export type AinectoEnv = \"prod\" | \"dev\";\n\nexport interface EndpointResolutionInput {\n env?: AinectoEnv;\n endpoint?: string;\n envVars?: NodeJS.ProcessEnv;\n}\n\nexport interface ResolvedEndpoint {\n env: AinectoEnv;\n endpoint: string;\n source: \"flag\" | \"env\" | \"default\";\n}\n\n/**\n * 기본 MCP 엔드포인트.\n *\n * OAuth 메타데이터(issuer·resource·authorization_servers)도 ainecto.com 이다.\n */\nconst DEFAULT_ENDPOINTS: Record<AinectoEnv, string> = {\n prod: \"https://ainecto.com/mcp\",\n dev: \"https://dev.ainecto.com/mcp\",\n};\n\nexport function resolveEndpoint(input: EndpointResolutionInput = {}): ResolvedEndpoint {\n const envVars = input.envVars ?? process.env;\n const env = input.env ?? \"prod\";\n\n if (input.endpoint) {\n return { env, endpoint: normalizeEndpoint(input.endpoint), source: \"flag\" };\n }\n\n if (envVars.AINECTO_MCP_ENDPOINT) {\n return { env, endpoint: normalizeEndpoint(envVars.AINECTO_MCP_ENDPOINT), source: \"env\" };\n }\n\n return { env, endpoint: DEFAULT_ENDPOINTS[env], source: \"default\" };\n}\n\nexport function normalizeEndpoint(endpoint: string): string {\n const url = new URL(endpoint);\n assertTrustedEndpointUrl(url, \"MCP endpoint\");\n url.hash = \"\";\n return url.toString().replace(/\\/$/, \"\");\n}\n\n/**\n * ★<b>이 env 의 기본 주소와 «정확히» 같은가.</b>\n *\n * <p>{@link isDefaultEndpoint} 는 prod·dev 를 둘 다 «기본»으로 보는 넓은 판정이라,\n * 「env=prod 인데 endpoint 만 dev」 같은 입력을 기본으로 착각한다. 그 판정으로 설정에서\n * {@code --endpoint} 를 빼면, 조회한 주소와 세션이 붙는 주소가 갈라진다\n * (2026-09-23 독립 재리뷰 I7).\n */\nexport function isDefaultEndpointFor(env: AinectoEnv, endpoint: string): boolean {\n return normalizeEndpoint(endpoint) === DEFAULT_ENDPOINTS[env];\n}\n\nexport function isDefaultEndpoint(endpoint: string): boolean {\n const normalized = normalizeEndpoint(endpoint);\n return Object.values(DEFAULT_ENDPOINTS).includes(normalized);\n}\n\nexport function assertTrustedEndpointUrl(value: string | URL, label = \"URL\"): void {\n const url = typeof value === \"string\" ? new URL(value) : value;\n if (url.protocol === \"https:\") {\n return;\n }\n if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) {\n return;\n }\n throw new Error(`${label} must use https, except http is allowed for localhost loopback endpoints.`);\n}\n\nexport function assertLoopbackRedirectUrl(value: string): void {\n const url = new URL(value);\n if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) {\n return;\n }\n throw new Error(\"OAuth redirect URI must be an http localhost loopback URL.\");\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n return hostname === \"localhost\"\n || hostname === \"127.0.0.1\"\n || hostname === \"::1\"\n || hostname === \"[::1]\";\n}\n\nexport function assertEnv(value: string | undefined): AinectoEnv | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (value === \"prod\" || value === \"dev\") {\n return value;\n }\n throw new Error(`Invalid --env value \"${value}\". Expected \"prod\" or \"dev\".`);\n}\n\n/**\n * 직무 프로필을 고치는 설정 화면 — ★이 MCP 서버와 같은 출처의 홈페이지 설정(기본 탭 = 계정).\n * 규칙은 웹 앱과 같다(ainecto-front {@code src/lib/homepage.ts} 의 {@code /home/settings}, 같은 출처 배포).\n */\nexport function profileSettingsUrl(endpoint: string): string {\n return `${new URL(endpoint).origin}/home/settings`;\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile, chmod } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport interface StoredTokenSet {\n endpoint: string;\n accessToken: string;\n refreshToken?: string;\n tokenType?: string;\n expiresAt?: number;\n scope?: string;\n clientId?: string;\n clientSecret?: string;\n authorizationServer?: string;\n updatedAt: string;\n}\n\n/**\n * 토큰 저장소 — ★서버(endpoint)마다 «한 칸»이다(설계 0.4.2 §2-4). 세션 역할은 없다(0.8.0).\n * 0.4.1 이하가 쓴 역할별 칸은 읽지도 지우지도 않는다(사용자 결정 «마이그레이션은 고려하지 마»).\n */\nexport interface TokenStore {\n load(endpoint: string): Promise<StoredTokenSet | undefined>;\n save(endpoint: string, token: StoredTokenSet): Promise<void>;\n delete(endpoint: string): Promise<void>;\n}\n\ntype TokenFile = Record<string, StoredTokenSet>;\n\nexport class FileTokenStore implements TokenStore {\n readonly filePath: string;\n\n constructor(rootDir = join(homedir(), \".ainecto\")) {\n this.filePath = join(rootDir, \"tokens.json\");\n }\n\n async load(endpoint: string): Promise<StoredTokenSet | undefined> {\n const file = await this.readFile();\n return file[tokenKey(endpoint)];\n }\n\n async save(endpoint: string, token: StoredTokenSet): Promise<void> {\n const file = await this.readFile();\n file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: new Date().toISOString() };\n await this.writeFile(file);\n }\n\n async delete(endpoint: string): Promise<void> {\n const file = await this.readFile();\n delete file[tokenKey(endpoint)];\n await this.writeFile(file);\n }\n\n private async readFile(): Promise<TokenFile> {\n try {\n await this.hardenExistingPermissions();\n const raw = await readFile(this.filePath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"Token file must contain an object.\");\n }\n return parsed as TokenFile;\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return {};\n }\n throw error;\n }\n }\n\n private async writeFile(file: TokenFile): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true, mode: 0o700 });\n await chmod(dirname(this.filePath), 0o700);\n const tmpPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tmpPath, `${JSON.stringify(file, null, 2)}\\n`, { mode: 0o600 });\n await chmod(tmpPath, 0o600);\n await rename(tmpPath, this.filePath);\n await chmod(this.filePath, 0o600);\n }\n\n private async hardenExistingPermissions(): Promise<void> {\n const dirPath = dirname(this.filePath);\n await chmodIfPermissive(dirPath, 0o700);\n await chmodIfPermissive(this.filePath, 0o600);\n }\n}\n\n/** 서버 하나의 칸. 0.4.1 이하의 «역할 없는» 칸과 같은 키다 — 그때 역할 없이 로그인해 둔 사람은 그대로 쓴다. */\nexport function tokenKey(endpoint: string): string {\n return createHash(\"sha256\").update(endpoint).digest(\"hex\");\n}\n\nexport async function tokenStorePermissions(path: string): Promise<{ mode: number } | undefined> {\n try {\n const result = await stat(path);\n return { mode: result.mode & 0o777 };\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function tokenStoreDirectoryPermissions(path: string): Promise<{ mode: number } | undefined> {\n return tokenStorePermissions(dirname(path));\n}\n\nexport async function removeTokenStoreForTests(path: string): Promise<void> {\n await rm(dirname(path), { recursive: true, force: true });\n}\n\nfunction isNodeError(error: unknown, code: string): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === code;\n}\n\nasync function chmodIfPermissive(path: string, mode: number): Promise<void> {\n try {\n const result = await stat(path);\n if ((result.mode & 0o077) !== 0) {\n await chmod(path, mode);\n }\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return;\n }\n throw error;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { createServer } from \"node:http\";\nimport { spawn, type ChildProcess, type SpawnOptions } from \"node:child_process\";\nimport { URLSearchParams } from \"node:url\";\nimport type { StoredTokenSet, TokenStore } from \"./tokenStore\";\nimport { registerPublicClient, type ClientRegistrationResult } from \"./clientRegistration\";\nimport { assertTrustedEndpointUrl, isDefaultEndpoint } from \"../config/endpoints\";\n\n/**\n * ★로그인은 서버마다 한 번이다(설계 0.4.2). 토큰은 역할을 싣지 않고, 역할은 저장소에서 와서\n * 요청 헤더로 실린다({@link McpRpcClient}). 그래서 이 클라이언트에는 역할이 없다.\n */\nexport interface OAuthClientOptions {\n endpoint: string;\n tokenStore: TokenStore;\n fetchImpl?: typeof fetch;\n envVars?: NodeJS.ProcessEnv;\n /**\n * 브라우저를 연다. <b>열기에 실패하면 거절</b>하고, 성공이 확인되면(또는 아직 모르면) 가만히 있는다.\n * login 은 이 약속을 «기다리지 않고» 콜백 대기와 경주시킨다 — 여는 명령이 브라우저가 닫힐 때까지\n * 안 끝나는 환경에서 순서대로 기다리면 교착한다.\n */\n openBrowser?: (url: string) => Promise<void>;\n /**\n * 인가 URL 을 «브라우저를 열기 전에» 알린다. CLI 는 stderr 에 한 줄 쓴다 — 브라우저가 안 열려도\n * 사람이 직접 열 수 있고, 에이전트가 실행했다면 명령이 끝날 때 그 줄이 에이전트에게 닿는다.\n */\n onAuthorizeUrl?: (url: string) => void;\n /** 콜백을 기다리는 한도. 기본 5분 — 사람이 로그인하기엔 넉넉하고, 에이전트 셸 도구 최대 한도보다 짧다. */\n loginTimeoutMs?: number;\n /**\n * 토큰 저장소를 «읽기만» 한다 — 갱신하지 않는다(네트워크도, 토큰 파일 쓰기도 없다).\n * {@code init --dry-run} 이 쓴다: 「아무것도 바꾸지 않는다」에 사용자의 로그인 상태도 든다.\n * 갱신이 필요한(만료된) 토큰은 «없음»으로 본다.\n */\n readOnly?: boolean;\n}\n\n/** 로그인 대기 기본 한도(5분). */\nexport const DEFAULT_LOGIN_TIMEOUT_MS = 300_000;\n\n/**\n * 로그인이 «사람 쪽 사정으로» 끝나지 못한 이유. {@code code} 는 {@code --json} 의 {@code error.code} 로\n * 그대로 나간다(종료 코드는 다른 실패와 같은 1 — 받는 쪽은 메시지를 읽고 사람에게 옮긴다).\n */\nexport class OAuthLoginError extends Error {\n constructor(readonly code: \"LOGIN_TIMEOUT\" | \"BROWSER_UNAVAILABLE\" | \"CALLBACK_UNAVAILABLE\", message: string) {\n super(message);\n this.name = \"OAuthLoginError\";\n }\n}\n\nexport interface OAuthMetadata {\n authorizationEndpoint: string;\n tokenEndpoint: string;\n registrationEndpoint?: string;\n issuer?: string;\n codeChallengeMethodsSupported: string[];\n}\n\nexport interface TokenProvider {\n getAccessToken(): Promise<string | undefined>;\n refreshAfterUnauthorized(): Promise<string | undefined>;\n}\n\nexport class OAuthClient implements TokenProvider {\n private readonly fetchImpl: typeof fetch;\n private readonly envVars: NodeJS.ProcessEnv;\n private readonly openBrowserImpl: (url: string) => Promise<void>;\n\n constructor(private readonly options: OAuthClientOptions) {\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.envVars = options.envVars ?? process.env;\n this.openBrowserImpl = options.openBrowser ?? openBrowser;\n }\n\n async getAccessToken(): Promise<string | undefined> {\n const envToken = this.envVars.AINECTO_TOKEN;\n if (envToken) {\n assertEnvTokenEndpointAllowed(this.options.endpoint, this.envVars);\n return envToken;\n }\n\n const stored = await this.options.tokenStore.load(this.options.endpoint);\n if (!stored) {\n return undefined;\n }\n\n if (!shouldRefresh(stored)) {\n return stored.accessToken;\n }\n if (this.options.readOnly) {\n return isExpired(stored) ? undefined : stored.accessToken;\n }\n // ★만료됐는데 갱신할 수단이 없으면 «토큰 없음»이다. 예전엔 만료된 토큰을 그대로 돌려줘서,\n // init 은 로그인 대신 401 안내로 떨어졌고 다른 경로는 받을 게 뻔한 401 을 받으러 갔다\n // (2026-09-29 독립 리뷰 P1-2). 아직 60초 창 안이면 쓸 수 있으므로 돌려준다.\n if (!stored.refreshToken) {\n return isExpired(stored) ? undefined : stored.accessToken;\n }\n\n return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });\n }\n\n async refreshAfterUnauthorized(): Promise<string | undefined> {\n const stored = await this.options.tokenStore.load(this.options.endpoint);\n if (this.options.readOnly || !stored?.refreshToken) {\n return undefined;\n }\n return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });\n }\n\n async login(): Promise<StoredTokenSet> {\n const pkce = generatePkcePair();\n const state = generateOAuthState();\n const loopback = await createLoopbackReceiver(state);\n\n try {\n const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);\n const registration = metadata.registrationEndpoint\n ? await registerPublicClient(metadata.registrationEndpoint, loopback.redirectUri, this.fetchImpl)\n : { clientId: \"ainecto-cli\" };\n const authorizeUrl = new URL(metadata.authorizationEndpoint);\n authorizeUrl.searchParams.set(\"response_type\", \"code\");\n authorizeUrl.searchParams.set(\"client_id\", registration.clientId);\n authorizeUrl.searchParams.set(\"redirect_uri\", loopback.redirectUri);\n authorizeUrl.searchParams.set(\"code_challenge\", pkce.codeChallenge);\n authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authorizeUrl.searchParams.set(\"resource\", this.options.endpoint);\n authorizeUrl.searchParams.set(\"state\", state);\n // ★역할을 scope 에 싣지 않는다(설계 0.4.2) — 로그인은 서버마다 한 번이고, 역할은 저장소에서\n // 요청 헤더로 온다. 역할은 작업 가드레일이지 보안 경계가 아니다(2026-09-29 사용자 결정 Q1).\n\n assertTrustedEndpointUrl(authorizeUrl, \"OAuth authorization URL\");\n const url = authorizeUrl.toString();\n this.options.onAuthorizeUrl?.(url);\n // ★셋 중 먼저 오는 것이 결과다 — 콜백 도착 / 브라우저 열기 실패 / 시간 한도.\n // 예전엔 한도가 없고 열기 실패를 못 봐서, 브라우저가 없는 기계에서 말없이 영원히 기다렸다\n // (2026-09-29 독립 리뷰 P1-2). 비TTY(에이전트가 실행)에서는 그 대기가 곧 멈춤이다.\n const browserFailure = this.openBrowserImpl(url).then(() => new Promise<never>(() => undefined));\n const code = await withTimeout(\n Promise.race([loopback.waitForCode(), browserFailure]),\n this.options.loginTimeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS,\n );\n const token = await exchangeToken(metadata.tokenEndpoint, {\n grant_type: \"authorization_code\",\n code,\n redirect_uri: loopback.redirectUri,\n client_id: registration.clientId,\n code_verifier: pkce.codeVerifier,\n resource: this.options.endpoint,\n ...(registration.clientSecret ? { client_secret: registration.clientSecret } : {}),\n }, this.fetchImpl);\n\n const stored = toStoredToken(this.options.endpoint, token, metadata, registration);\n await this.options.tokenStore.save(this.options.endpoint, stored);\n return stored;\n } finally {\n await loopback.close();\n }\n }\n\n async logout(): Promise<void> {\n await this.options.tokenStore.delete(this.options.endpoint);\n }\n\n /** 부르는 쪽이 refresh token 이 있음을 이미 확인했다 — 타입이 그것을 요구한다. */\n private async refreshStoredToken(stored: StoredTokenSet & { refreshToken: string }): Promise<string | undefined> {\n const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);\n const token = await exchangeToken(metadata.tokenEndpoint, {\n grant_type: \"refresh_token\",\n refresh_token: stored.refreshToken,\n client_id: stored.clientId ?? \"ainecto-cli\",\n ...(stored.clientSecret ? { client_secret: stored.clientSecret } : {}),\n resource: this.options.endpoint,\n }, this.fetchImpl);\n const next = toStoredToken(this.options.endpoint, token, metadata, stored);\n await this.options.tokenStore.save(this.options.endpoint, next);\n return next.accessToken;\n }\n}\n\nexport function generatePkcePair(): { codeVerifier: string; codeChallenge: string } {\n const codeVerifier = randomBytes(32).toString(\"base64url\");\n const codeChallenge = createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n return { codeVerifier, codeChallenge };\n}\n\nexport function generateOAuthState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/** 이미 만료됐는가. 만료 시각이 없으면 만료되지 않은 것으로 본다(서버가 수명을 안 알려 준 토큰). */\nexport function isExpired(token: Pick<StoredTokenSet, \"expiresAt\">, now = Date.now()): boolean {\n return token.expiresAt !== undefined && token.expiresAt <= now;\n}\n\nasync function withTimeout<T>(work: Promise<T>, timeoutMs: number): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n const minutes = timeoutMs / 60_000;\n const span = Number.isInteger(minutes) ? `${minutes} minute${minutes === 1 ? \"\" : \"s\"}` : `${timeoutMs} ms`;\n reject(new OAuthLoginError(\n \"LOGIN_TIMEOUT\",\n `Sign-in was not completed within ${span}. Nothing was saved — run the same command again.`,\n ));\n }, timeoutMs);\n });\n try {\n return await Promise.race([work, timeout]);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function shouldRefresh(token: Pick<StoredTokenSet, \"expiresAt\">, now = Date.now()): boolean {\n if (!token.expiresAt) {\n return false;\n }\n return token.expiresAt - now < 60_000;\n}\n\nexport async function discoverOAuthMetadata(endpoint: string, fetchImpl: typeof fetch = fetch): Promise<OAuthMetadata> {\n assertTrustedEndpointUrl(endpoint, \"MCP endpoint\");\n const resourceMetadataUrl = await discoverResourceMetadataUrl(endpoint, fetchImpl);\n assertTrustedEndpointUrl(resourceMetadataUrl, \"OAuth protected resource metadata URL\");\n const resourceMetadata = await fetchJson<Record<string, unknown>>(resourceMetadataUrl, fetchImpl);\n const authorizationServers = resourceMetadata.authorization_servers;\n if (!Array.isArray(authorizationServers) || typeof authorizationServers[0] !== \"string\") {\n throw new Error(\"OAuth protected resource metadata did not include authorization_servers.\");\n }\n const authServer = authorizationServers[0];\n assertTrustedEndpointUrl(authServer, \"OAuth authorization server\");\n const metadataUrl = authServer.includes(\"/.well-known/\")\n ? authServer\n : `${authServer.replace(/\\/$/, \"\")}/.well-known/oauth-authorization-server`;\n assertTrustedEndpointUrl(metadataUrl, \"OAuth authorization server metadata URL\");\n const metadata = await fetchJson<Record<string, unknown>>(metadataUrl, fetchImpl);\n if (typeof metadata.authorization_endpoint !== \"string\" || typeof metadata.token_endpoint !== \"string\") {\n throw new Error(\"OAuth authorization server metadata is missing authorization_endpoint or token_endpoint.\");\n }\n assertTrustedEndpointUrl(metadata.authorization_endpoint, \"OAuth authorization endpoint\");\n assertTrustedEndpointUrl(metadata.token_endpoint, \"OAuth token endpoint\");\n if (typeof metadata.registration_endpoint === \"string\") {\n assertTrustedEndpointUrl(metadata.registration_endpoint, \"OAuth client registration endpoint\");\n }\n if (typeof metadata.issuer === \"string\") {\n assertTrustedEndpointUrl(metadata.issuer, \"OAuth issuer\");\n }\n const codeChallengeMethodsSupported = parseStringArray(metadata.code_challenge_methods_supported);\n if (!codeChallengeMethodsSupported.includes(\"S256\")) {\n throw new Error(\"OAuth authorization server metadata must support PKCE S256.\");\n }\n return {\n authorizationEndpoint: metadata.authorization_endpoint,\n tokenEndpoint: metadata.token_endpoint,\n registrationEndpoint: typeof metadata.registration_endpoint === \"string\" ? metadata.registration_endpoint : undefined,\n issuer: typeof metadata.issuer === \"string\" ? metadata.issuer : undefined,\n codeChallengeMethodsSupported,\n };\n}\n\nasync function discoverResourceMetadataUrl(endpoint: string, fetchImpl: typeof fetch): Promise<string> {\n const challengeUrl = new URL(endpoint);\n assertTrustedEndpointUrl(challengeUrl, \"MCP endpoint\");\n const response = await fetchImpl(challengeUrl, { method: \"GET\" });\n const challenge = response.headers.get(\"www-authenticate\");\n const fromChallenge = challenge ? parseBearerChallengeParam(challenge, \"resource_metadata\") : undefined;\n if (fromChallenge) {\n assertTrustedEndpointUrl(fromChallenge, \"OAuth protected resource metadata URL\");\n return fromChallenge;\n }\n const origin = new URL(endpoint).origin;\n const fallback = `${origin}/.well-known/oauth-protected-resource`;\n assertTrustedEndpointUrl(fallback, \"OAuth protected resource metadata URL\");\n return fallback;\n}\n\nexport function parseBearerChallengeParam(header: string, name: string): string | undefined {\n const pattern = new RegExp(`${name}=\"([^\"]+)\"`);\n return pattern.exec(header)?.[1];\n}\n\nasync function exchangeToken(\n tokenEndpoint: string,\n params: Record<string, string>,\n fetchImpl: typeof fetch,\n): Promise<Record<string, unknown>> {\n assertTrustedEndpointUrl(tokenEndpoint, \"OAuth token endpoint\");\n const response = await fetchImpl(tokenEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(params),\n });\n if (!response.ok) {\n throw new Error(`OAuth token exchange failed with HTTP ${response.status}.`);\n }\n return response.json() as Promise<Record<string, unknown>>;\n}\n\ntype TokenContext = Partial<Pick<StoredTokenSet, \"refreshToken\" | \"tokenType\" | \"expiresAt\" | \"scope\" | \"clientId\" | \"clientSecret\">>\n & Partial<ClientRegistrationResult>;\n\nfunction toStoredToken(\n endpoint: string,\n token: Record<string, unknown>,\n metadata: OAuthMetadata,\n previous?: TokenContext,\n): StoredTokenSet {\n if (typeof token.access_token !== \"string\") {\n throw new Error(\"OAuth token response did not include access_token.\");\n }\n const expiresIn = typeof token.expires_in === \"number\" ? token.expires_in : undefined;\n return {\n endpoint,\n accessToken: token.access_token,\n refreshToken: typeof token.refresh_token === \"string\" ? token.refresh_token : previous?.refreshToken,\n tokenType: typeof token.token_type === \"string\" ? token.token_type : previous?.tokenType,\n expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : previous?.expiresAt,\n scope: typeof token.scope === \"string\" ? token.scope : previous?.scope,\n clientId: previous?.clientId ?? \"ainecto-cli\",\n clientSecret: previous?.clientSecret,\n authorizationServer: metadata.issuer,\n updatedAt: new Date().toISOString(),\n };\n}\n\n/**\n * 로그인 콜백을 받는 loopback 서버. {@code host} 는 시험이 «바인딩 실패»를 만들 때만 바꾼다.\n */\nexport async function createLoopbackReceiver(expectedState: string, host = \"127.0.0.1\"): Promise<{\n redirectUri: string;\n waitForCode(): Promise<string>;\n close(): Promise<void>;\n}> {\n let resolveCode!: (code: string) => void;\n let rejectCode!: (error: Error) => void;\n const codePromise = new Promise<string>((resolve, reject) => {\n resolveCode = resolve;\n rejectCode = reject;\n });\n codePromise.catch(() => undefined);\n\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n const error = url.searchParams.get(\"error\");\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n if (error) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization failed. You can close this tab.\");\n rejectCode(new Error(`OAuth authorization failed: ${error}`));\n return;\n }\n if (!state || state !== expectedState) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization state mismatch. You can close this tab.\");\n rejectCode(new Error(\"OAuth state mismatch.\"));\n return;\n }\n if (!code) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"Missing OAuth code.\");\n rejectCode(new Error(\"OAuth redirect did not include a code.\"));\n return;\n }\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization complete. You can close this tab.\");\n resolveCode(code);\n });\n\n // ★바인딩이 막히면(에이전트 sandbox 등) 예전엔 error 리스너가 없어 처리되지 않은 예외로 죽었다\n // (2026-09-29 코드 리뷰 P1). 사람이 할 일을 말하는 오류로 바꾼다.\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", (error) => reject(new OAuthLoginError(\n \"CALLBACK_UNAVAILABLE\",\n `Could not start the local sign-in callback on ${host} (${error.message}). `\n + \"If this command runs in a sandbox, allow local network binding or run it outside the sandbox.\",\n )));\n server.listen(0, host, () => resolve());\n });\n const address = server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"Loopback OAuth server did not bind to a TCP port.\");\n }\n return {\n redirectUri: `http://127.0.0.1:${address.port}/callback`,\n waitForCode: () => codePromise,\n close: () => new Promise<void>((resolve, reject) => {\n // Node 18 은 브라우저가 붙들고 있는 keep-alive 연결이 끝날 때까지 close 가 늦어진다.\n server.closeAllConnections?.();\n server.close((error) => error ? reject(error) : resolve());\n }),\n };\n}\n\nfunction parseStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n}\n\nasync function fetchJson<T>(url: string, fetchImpl: typeof fetch): Promise<T> {\n assertTrustedEndpointUrl(url, \"OAuth metadata URL\");\n const response = await fetchImpl(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch OAuth metadata from ${url}: HTTP ${response.status}.`);\n }\n return response.json() as Promise<T>;\n}\n\nexport function getBrowserOpenCommand(\n url: string,\n platform: NodeJS.Platform = process.platform,\n): { command: string; args: string[] } {\n assertTrustedEndpointUrl(url, \"OAuth authorization URL\");\n if (platform === \"darwin\") {\n return { command: \"open\", args: [url] };\n }\n if (platform === \"win32\") {\n return { command: \"explorer.exe\", args: [url] };\n }\n return { command: \"xdg-open\", args: [url] };\n}\n\nexport interface LaunchBrowserDeps {\n platform?: NodeJS.Platform;\n spawnImpl?: (command: string, args: string[], options: SpawnOptions) => ChildProcess;\n}\n\n/**\n * 기본 브라우저 열기. <b>여는 명령이 없거나(ENOENT) 0 이 아닌 코드로 끝나면 거절</b>한다.\n *\n * <p>★예전엔 {@code error} 리스너가 없어 명령이 없으면 처리되지 않은 오류로 죽었고, 명령은 있는데\n * 브라우저가 없으면({@code xdg-open} 비0 종료) 아무 말 없이 콜백을 영원히 기다렸다\n * (2026-09-29 독립 리뷰 P1-2).\n *\n * <p>⚠<b>Windows 는 종료 코드를 보지 않는다.</b> {@code explorer.exe} 는 성공해도 1 로 끝나는 것으로\n * 알려져 있어, 그 코드로 판정하면 정상 환경을 실패로 만든다 — 명령이 «떴다»는 것만 본다\n * (설계 §8-1, Windows 실측 V14 전까지 추정).\n */\nexport function launchBrowser(url: string, deps: LaunchBrowserDeps = {}): Promise<void> {\n const platform = deps.platform ?? process.platform;\n const { command, args } = getBrowserOpenCommand(url, platform);\n const spawnImpl = deps.spawnImpl ?? spawn;\n return new Promise<void>((resolve, reject) => {\n const unavailable = (reason: string) => new OAuthLoginError(\n \"BROWSER_UNAVAILABLE\",\n `Could not open a browser on this machine (${command}: ${reason}). Sign-in needs a browser here.`,\n );\n let child: ChildProcess;\n try {\n child = spawnImpl(command, args, { stdio: \"ignore\", detached: true });\n } catch (error) {\n reject(unavailable(error instanceof Error ? error.message : String(error)));\n return;\n }\n child.once(\"error\", (error) => reject(unavailable(error.message)));\n if (platform === \"win32\") {\n child.once(\"spawn\", () => resolve());\n } else {\n child.once(\"exit\", (code, signal) => {\n if (code === 0) {\n resolve();\n } else {\n reject(unavailable(code === null ? `stopped by ${signal ?? \"a signal\"}` : `exited with code ${code}`));\n }\n });\n }\n child.unref();\n });\n}\n\nfunction openBrowser(url: string): Promise<void> {\n return launchBrowser(url);\n}\n\nfunction assertEnvTokenEndpointAllowed(endpoint: string, envVars: NodeJS.ProcessEnv): void {\n if (envVars.AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN === \"1\") {\n return;\n }\n if (isDefaultEndpoint(endpoint)) {\n return;\n }\n throw new Error(\"AINECTO_TOKEN can only be used with the default prod/dev endpoints. Set AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN=1 only when you intentionally trust the custom endpoint.\");\n}\n","import { assertLoopbackRedirectUrl, assertTrustedEndpointUrl } from \"../config/endpoints\";\n\nexport interface ClientRegistrationResult {\n clientId: string;\n clientSecret?: string;\n}\n\nexport async function registerPublicClient(\n registrationEndpoint: string,\n redirectUri: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<ClientRegistrationResult> {\n assertTrustedEndpointUrl(registrationEndpoint, \"OAuth client registration endpoint\");\n assertLoopbackRedirectUrl(redirectUri);\n const response = await fetchImpl(registrationEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({\n client_name: \"AiNecto CLI\",\n redirect_uris: [redirectUri],\n token_endpoint_auth_method: \"none\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n }),\n });\n if (!response.ok) {\n throw new Error(`OAuth client registration failed with HTTP ${response.status}.`);\n }\n const body = await response.json() as unknown;\n if (!isRecord(body) || typeof body.client_id !== \"string\") {\n throw new Error(\"OAuth client registration response did not include client_id.\");\n }\n return {\n clientId: body.client_id,\n clientSecret: typeof body.client_secret === \"string\" ? body.client_secret : undefined,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * 옛 세션 역할 입력 — ★0.8.0 에서 세션 역할(Design/Development/Test/Validation)이 없어졌다.\n * 도구 목록은 모든 세션이 같고, 무엇을 할 수 있는지는 서버가 사용자·리소스 권한으로만 정한다.\n *\n * <p>그래도 옛 입력은 «거절하지 않는다» — 0.7.x 의 {@code init} 이 쓴 저장소 설정\n * ({@code npx -y @ainecto/mcp --role design}, 버전 고정 없음)은 게시 즉시 이 판을 띄운다.\n * 여기서 {@code --role} 을 거절하면 그 저장소 전부의 연결이 끊긴다(설계 §2-3 R1).\n *\n * <p>받은 값은 «검사하지 않고» 두 군데에만 쓴다:\n * <ul>\n * <li>stderr 안내 한 줄({@link LEGACY_ROLE_NOTICE}) — 사람이 설정을 정리하게.</li>\n * <li>같은 헤더 {@link LEGACY_ROLE_HEADER} 로 서버에 실어 보냄 — 서버는 판정에 쓰지 않고\n * {@code [mcp][legacy-role]} 로 기록만 한다. 새 헤더를 만들지 않으려고 옛 이름을 재사용한다.</li>\n * </ul>\n * ★관찰 기간(설계 §7-2 ⑤) 뒤 기록이 0건이면 이 파일과 브리지의 {@code --role} 받기를 함께 지운다.\n */\n\n/** 서버가 기록용으로 읽는 헤더 — 서버 McpController 의 옛 역할 헤더와 같은 이름. */\nexport const LEGACY_ROLE_HEADER = \"X-AiNecto-Role\";\n\n/** 0.7.x 가 Codex 처럼 전역 설정뿐인 에이전트에 안내하던 환경변수. */\nexport const LEGACY_ROLE_ENV_VAR = \"AINECTO_ROLE\";\n\nexport const LEGACY_ROLE_NOTICE =\n \"AiNecto session roles were removed; access follows your AiNecto permissions. \"\n + \"Re-run `ainecto init` to tidy this repository.\";\n\n/**\n * 플래그(있으면) 아니면 {@link LEGACY_ROLE_ENV_VAR}. ★값을 검사하지 않는다 — 모르는 값도 받는다.\n * 빈 값은 «없음»이다.\n */\nexport function legacyRoleInput(flag: string | undefined, envVars: NodeJS.ProcessEnv = process.env): string | undefined {\n const value = flag ?? envVars[LEGACY_ROLE_ENV_VAR];\n return value !== undefined && value.trim() ? value.trim() : undefined;\n}\n","/**\n * CLI 버전. ★진실원은 {@code package.json} 이고 빌드(tsup define)가 그 값을 박는다 —\n * 상수로 또 적으면 릴리스 때 한쪽만 올라가 조용히 거짓이 된다.\n * 번들을 거치지 않는 실행(vitest·tsx)에서는 값이 없으므로 \"dev\" 로 떨어진다.\n */\ndeclare const __AINECTO_CLI_VERSION__: string | undefined;\n\n/** 번들을 거치지 않은 실행의 판 표시. */\nconst UNBUNDLED_VERSION = \"dev\";\n\nexport const CLI_VERSION: string =\n typeof __AINECTO_CLI_VERSION__ === \"string\" ? __AINECTO_CLI_VERSION__ : UNBUNDLED_VERSION;\n\n/**\n * npm 주 이름. ★이 이름을 박는 곳은 여기 하나다 — init·setup 이 쓰는 {@code npx -y <이름>@<판>},\n * MCP {@code clientInfo}, User-Agent, init 기록의 {@code generatedBy} 가 모두 이것을 쓴다.\n * {@code package.json} 의 name·{@code server.json} 의 패키지 식별자와 같아야 한다(시험이 대조한다).\n *\n * <p>다른 이름 {@code ainecto}·{@code @ainecto/cli} 에도 같은 코드가 올라가지만(scripts/publish.mjs),\n * 그 판으로 init 해도 이 이름을 쓴다.\n */\nexport const PACKAGE_NAME = \"@ainecto/mcp\";\n\nexport const CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;\n\n/**\n * MCP 설정에 쓰는 브리지 패키지 지정 — ★이 CLI 판으로 «고정»한다(설계 결정 #6·D8). 고정하지 않으면\n * 게시 즉시 모든 저장소의 브리지가 새 판으로 바뀐다(0.7.x 의 --role 설정이 그랬다). 올리는 길은\n * {@code setup}/{@code init} 재실행이다. 판을 모르는 실행(vitest·tsx)에서는 고정하지 않는다 —\n * {@code @dev} 는 npm 에서 판이 아니라 배포 태그로 읽힌다.\n */\nexport function bridgePackageSpec(version: string = CLI_VERSION): string {\n return version === UNBUNDLED_VERSION ? PACKAGE_NAME : `${PACKAGE_NAME}@${version}`;\n}\n","import type { TokenProvider } from \"../auth/oauth\";\nimport { LEGACY_ROLE_HEADER } from \"../harness/legacyRole\";\nimport { CLI_VERSION, PACKAGE_NAME } from \"../version\";\n\nexport interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcSuccess {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n result: unknown;\n}\n\nexport interface JsonRpcFailure {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport type JsonRpcResponse = JsonRpcSuccess | JsonRpcFailure;\n\n/**\n * 분석용 요청 헤더(GA4 서버 계측 C23). 서버의 ExecutionChannel.HEADER · AgentTypeResolver.HEADER 와 같아야 한다.\n * 서버는 /mcp 필터에서 한 번만 읽고, 모르는 값이면 요청을 막지 않는다(분석 값일 뿐이다).\n */\nexport const CHANNEL_HEADER = \"X-AiNecto-Channel\";\nexport const AGENT_HEADER = \"X-AiNecto-Agent\";\n/** 서버 AgentTypeResolver.MAX_NAME_LENGTH 와 같다. */\nexport const MAX_AGENT_NAME_LENGTH = 100;\n\n/** 이 클라이언트가 서버에 밝히는 길 — 직접 명령(`ainecto tools call …`) = cli, 에이전트의 MCP 브리지 = mcp. */\nexport type ExecutionChannel = \"cli\" | \"mcp\";\n\nexport interface McpRpcClientOptions {\n endpoint: string;\n tokenProvider?: TokenProvider;\n fetchImpl?: typeof fetch;\n envVars?: NodeJS.ProcessEnv;\n /**\n * 옛 {@code --role}/{@code AINECTO_ROLE} 값 — ★기록용이다(legacyRole.ts). 있으면 모든 요청의\n * {@code X-AiNecto-Role} 헤더로 실리고, 서버는 판정에 쓰지 않고 로그로만 남긴다.\n */\n legacyRole?: string;\n /**\n * 요청의 길. ★필수다 — 빠뜨리면 서버가 직접 명령을 MCP 로 센다. 그 빠뜨림을 타입이 잡는다.\n * 모든 요청의 {@code X-AiNecto-Channel} 헤더로 실린다.\n */\n channel: ExecutionChannel;\n}\n\nexport class McpRpcError extends Error {\n constructor(\n message: string,\n readonly code: string,\n readonly details?: unknown,\n ) {\n super(message);\n }\n}\n\nexport class McpRpcClient {\n private readonly fetchImpl: typeof fetch;\n private nextId = 1;\n private agent: string | undefined;\n\n constructor(private readonly options: McpRpcClientOptions) {\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n /**\n * 브리지 전용 — 호스트 에이전트가 보낸 {@code initialize} 의 {@code clientInfo.name}(프로그램 고정 이름).\n * 이후 모든 요청의 {@code X-AiNecto-Agent} 헤더로 싣는다. 직접 명령은 부르지 않는다(누가 셸을 돌렸는지 모른다).\n */\n useAgent(clientName: unknown): void {\n this.agent = agentHeaderValue(clientName);\n }\n\n initialize(): Promise<unknown> {\n return this.request(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION },\n });\n }\n\n async toolsList(): Promise<unknown[]> {\n const result = await this.request(\"tools/list\", {});\n if (!isRecord(result) || !Array.isArray(result.tools)) {\n throw new McpRpcError(\"tools/list response did not include a tools array.\", \"MCP_PROTOCOL_ERROR\", result);\n }\n return result.tools;\n }\n\n async toolsCall(name: string, args: unknown): Promise<unknown> {\n return this.request(\"tools/call\", { name, arguments: args ?? {} });\n }\n\n async request(method: string, params?: unknown): Promise<unknown> {\n const response = await this.sendRaw({\n jsonrpc: \"2.0\",\n id: this.nextId++,\n method,\n params,\n });\n if (!response) {\n return undefined;\n }\n if (\"error\" in response) {\n throw new McpRpcError(response.error.message, \"MCP_ERROR\", response.error);\n }\n return response.result;\n }\n\n async sendRaw(request: JsonRpcRequest): Promise<JsonRpcResponse | undefined> {\n const token = await this.options.tokenProvider?.getAccessToken();\n let response = await this.postJson(request, token);\n if (response.status === 401 && this.options.tokenProvider) {\n const refreshed = await this.options.tokenProvider.refreshAfterUnauthorized();\n if (refreshed) {\n response = await this.postJson(request, refreshed);\n }\n }\n\n if (response.status === 204 || response.status === 202) {\n return undefined;\n }\n const text = await response.text();\n if (!response.ok) {\n throw new McpRpcError(\n `MCP HTTP request failed with HTTP ${response.status}.`,\n \"MCP_HTTP_ERROR\",\n httpErrorDetails(response.status, text, this.options.envVars ?? process.env),\n );\n }\n if (!text.trim()) {\n return undefined;\n }\n const parsed = JSON.parse(text) as unknown;\n if (!isJsonRpcResponse(parsed)) {\n throw new McpRpcError(\"MCP response was not a JSON-RPC response.\", \"MCP_PROTOCOL_ERROR\", parsed);\n }\n return parsed;\n }\n\n private postJson(request: JsonRpcRequest, token: string | undefined): Promise<Response> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n };\n if (token) {\n headers.authorization = `Bearer ${token}`;\n }\n const legacyRole = printableHeaderValue(this.options.legacyRole);\n if (legacyRole) {\n headers[LEGACY_ROLE_HEADER.toLowerCase()] = legacyRole;\n }\n headers[CHANNEL_HEADER.toLowerCase()] = this.options.channel;\n if (this.agent) {\n headers[AGENT_HEADER.toLowerCase()] = this.agent;\n }\n return this.fetchImpl(this.options.endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify(request),\n });\n }\n}\n\n/**\n * ★MCP 는 «도구가 거부/실패했다»를 JSON-RPC error 가 아니라 결과 안의 {@code isError} 로\n * 알린다(2025-06-18 규격). 그래서 최상위 error 만 보면 실패가 성공으로 보고된다 —\n * 셸 자동화는 exit 0 을 받고 다음 단계로 간다(2026-09-22 독립 리뷰 I9).\n */\nexport function isToolError(result: unknown): boolean {\n return isRecord(result) && result.isError === true;\n}\n\n/**\n * 헤더에 실을 수 있는 이름 — 인쇄 가능한 ASCII 만(그 밖의 글자는 fetch 가 헤더로 거절한다), 앞뒤 공백 제거,\n * 최대 {@link MAX_AGENT_NAME_LENGTH}자. 남는 것이 없으면 싣지 않는다(서버는 unknown 으로 센다).\n */\nexport function agentHeaderValue(clientName: unknown): string | undefined {\n return printableHeaderValue(clientName);\n}\n\n/** 헤더에 실을 수 있는 모양으로 — 규칙은 {@link agentHeaderValue} 와 같다(이 파일의 헤더 값은 전부 이 길을 지난다). */\nfunction printableHeaderValue(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const printable = value.replace(/[^\\x20-\\x7E]/g, \"\").trim().slice(0, MAX_AGENT_NAME_LENGTH).trim();\n return printable || undefined;\n}\n\nfunction isJsonRpcResponse(value: unknown): value is JsonRpcResponse {\n return isRecord(value)\n && value.jsonrpc === \"2.0\"\n && (\"result\" in value || \"error\" in value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction httpErrorDetails(status: number, body: string, envVars: NodeJS.ProcessEnv): Record<string, unknown> {\n const details: Record<string, unknown> = { status };\n if (envVars.AINECTO_CLI_DEBUG === \"1\" || envVars.AINECTO_DEBUG === \"1\") {\n details.body = safeBodyPreview(body);\n }\n return details;\n}\n\nexport function safeBodyPreview(body: string): string {\n return body.length > 500 ? `${body.slice(0, 500)}...` : body;\n}\n","import { createInterface } from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { McpRpcClient, type JsonRpcRequest } from \"./rpcClient\";\n\nexport interface StdioBridgeOptions {\n input: Readable;\n output: Writable;\n errorOutput: Writable;\n client: McpRpcClient;\n}\n\nexport async function runStdioBridge(options: StdioBridgeOptions): Promise<void> {\n const lines = createInterface({ input: options.input, crlfDelay: Infinity });\n\n for await (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n try {\n const request = parseJsonRpcRequest(line);\n if (request.method === \"initialize\") {\n // 호스트 에이전트의 고정 이름 — 이 세션의 모든 요청에 X-AiNecto-Agent 로 실린다(initialize 자신 포함).\n options.client.useAgent(clientInfoName(request.params));\n }\n const response = await options.client.sendRaw(request);\n if (response && request.id !== undefined) {\n options.output.write(`${JSON.stringify(response)}\\n`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n options.output.write(`${JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32603, message },\n })}\\n`);\n }\n }\n}\n\nfunction parseJsonRpcRequest(line: string): JsonRpcRequest {\n const parsed = JSON.parse(line) as unknown;\n if (!isRecord(parsed) || parsed.jsonrpc !== \"2.0\" || typeof parsed.method !== \"string\") {\n throw new Error(\"Invalid JSON-RPC request.\");\n }\n return {\n jsonrpc: \"2.0\",\n id: typeof parsed.id === \"string\" || typeof parsed.id === \"number\" || parsed.id === null ? parsed.id : undefined,\n method: parsed.method,\n params: parsed.params,\n };\n}\n\nfunction clientInfoName(params: unknown): unknown {\n return isRecord(params) && isRecord(params.clientInfo) ? params.clientInfo.name : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { FileTokenStore } from \"../../core/auth/tokenStore\";\nimport { OAuthClient } from \"../../core/auth/oauth\";\nimport { McpRpcClient } from \"../../core/mcp/rpcClient\";\nimport { runStdioBridge } from \"../../core/mcp/stdioBridge\";\nimport { LEGACY_ROLE_NOTICE, legacyRoleInput } from \"../../core/harness/legacyRole\";\nimport type { Readable, Writable } from \"node:stream\";\n\nexport interface ConnectorOptions {\n endpoint: string;\n /** 옛 MCP 설정의 {@code --role} 값(검사하지 않는다). 없으면 {@code AINECTO_ROLE} 를 본다 — 기록용. */\n legacyRoleFlag?: string;\n envVars?: NodeJS.ProcessEnv;\n input: Readable;\n output: Writable;\n errorOutput: Writable;\n}\n\nexport async function runConnector(options: ConnectorOptions): Promise<void> {\n // ★토큰은 서버마다 한 칸이다. 세션 역할은 없다 — 도구 목록·권한은 서버가 사용자 권한으로 정한다.\n const legacyRole = legacyRoleInput(options.legacyRoleFlag, options.envVars);\n if (legacyRole !== undefined) {\n // stdout 은 JSON-RPC 전용이다 — 안내는 stderr 로만.\n options.errorOutput.write(`${LEGACY_ROLE_NOTICE}\\n`);\n }\n const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore: new FileTokenStore() });\n const client = new McpRpcClient({\n endpoint: options.endpoint,\n tokenProvider: auth,\n legacyRole,\n // 에이전트의 MCP 연결 — 에이전트 이름은 브리지가 호스트의 initialize 에서 읽어 싣는다.\n channel: \"mcp\",\n });\n await runStdioBridge({\n input: options.input,\n output: options.output,\n errorOutput: options.errorOutput,\n client,\n });\n}\n","export interface CliErrorShape {\n ok: false;\n error: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\nexport interface CliSuccessShape {\n ok: true;\n data: unknown;\n warnings?: string[];\n}\n\nexport function renderSuccess(data: unknown, options: { json?: boolean; warnings?: string[] } = {}): string {\n if (options.json) {\n const shape: CliSuccessShape = { ok: true, data };\n if (options.warnings?.length) {\n shape.warnings = options.warnings;\n }\n return `${JSON.stringify(shape, null, 2)}\\n`;\n }\n const warnings = options.warnings?.length ? `${options.warnings.map((warning) => `Warning: ${warning}`).join(\"\\n\")}\\n` : \"\";\n return `${warnings}${renderHuman(data)}\\n`;\n}\n\nexport function renderError(error: unknown, options: { json?: boolean } = {}): string {\n const shape = toErrorShape(error);\n if (options.json) {\n return `${JSON.stringify(shape, null, 2)}\\n`;\n }\n return `Error: ${shape.error.message}\\n`;\n}\n\nexport function toErrorShape(error: unknown): CliErrorShape {\n if (error instanceof Error) {\n const maybe = error as Error & { code?: string; details?: unknown };\n return {\n ok: false,\n error: {\n code: maybe.code ?? \"CLI_ERROR\",\n message: error.message,\n details: redactSecrets(maybe.details),\n },\n };\n }\n return { ok: false, error: { code: \"CLI_ERROR\", message: String(error) } };\n}\n\nexport function redactSecrets(value: unknown): unknown {\n return redactSecretsInner(value, new WeakSet<object>());\n}\n\nfunction renderHuman(data: unknown): string {\n if (Array.isArray(data)) {\n return data.map((item) => renderHuman(item)).join(\"\\n\");\n }\n if (typeof data === \"string\") {\n return data;\n }\n return JSON.stringify(data, null, 2);\n}\n\nfunction redactSecretsInner(value: unknown, seen: WeakSet<object>): unknown {\n if (typeof value === \"string\") {\n return redactSecretString(value);\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactSecretsInner(item, seen));\n }\n if (value && typeof value === \"object\") {\n if (seen.has(value)) {\n return \"[circular]\";\n }\n seen.add(value);\n const output: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n output[key] = isSecretKey(key) ? \"[redacted]\" : redactSecretsInner(child, seen);\n }\n return output;\n }\n return value;\n}\n\nfunction isSecretKey(key: string): boolean {\n const normalized = key.toLowerCase().replace(/-/g, \"_\");\n return normalized === \"authorization\"\n || normalized.includes(\"access_token\")\n || normalized.includes(\"refresh_token\")\n || normalized.includes(\"client_secret\")\n || normalized.includes(\"token\");\n}\n\nfunction redactSecretString(value: string): string {\n return value\n .replace(/(\"(?:authorization|access_token|refresh_token|client_secret|token)\"\\s*:\\s*\")([^\"]*)(\")/gi, \"$1[redacted]$3\")\n .replace(/((?:authorization|access_token|refresh_token|client_secret|token)=)([^&\\s\"']+)/gi, \"$1[redacted]\")\n .replace(/(authorization:\\s*bearer\\s+)([^\\s,}]+)/gi, \"$1[redacted]\")\n .replace(/(bearer\\s+)(eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+)/gi, \"$1[redacted]\");\n}\n","import { assertEnv, type AinectoEnv } from \"../../core/config/endpoints\";\n\nexport interface McpBinArgs {\n env?: AinectoEnv;\n endpoint?: string;\n /**\n * 옛 {@code --role} 값 — ★검사하지 않는다(legacyRole.ts). 0.7.x 가 쓴 저장소 설정이 이 판을 띄우므로\n * 여기서 멈추면 그 연결이 전부 끊긴다. 값 없이 끝난 {@code --role} 도 멈추지 않고 «없음»으로 둔다.\n */\n legacyRole?: string;\n help: boolean;\n}\n\nexport function parseMcpArgs(argv: string[]): McpBinArgs {\n const result: McpBinArgs = { help: false };\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (!arg) {\n continue;\n }\n if (arg === \"--env\") {\n result.env = assertEnv(requireFlagValue(arg, argv[++index]));\n } else if (arg === \"--endpoint\") {\n result.endpoint = requireFlagValue(arg, argv[++index]);\n } else if (arg === \"--role\") {\n const value = argv[index + 1];\n if (value !== undefined && !value.startsWith(\"--\")) {\n result.legacyRole = value;\n index += 1;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n result.help = true;\n } else {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n return result;\n}\n\nfunction requireFlagValue(flag: string, value: string | undefined): string {\n if (!value || value.startsWith(\"--\")) {\n throw new Error(`${flag} requires a value.`);\n }\n return value;\n}\n","#!/usr/bin/env node\nimport { resolveEndpoint } from \"../core/config/endpoints\";\nimport { runConnector } from \"../adapters/mcp/connector\";\nimport { renderError } from \"../core/output/render\";\nimport { parseMcpArgs } from \"../adapters/mcp/args\";\n\ntry {\n const args = parseMcpArgs(process.argv.slice(2));\n if (args.help) {\n process.stdout.write(\"Usage: mcp [--env prod|dev] [--endpoint URL]\\n\");\n process.exit(0);\n }\n const resolved = resolveEndpoint({ env: args.env, endpoint: args.endpoint });\n await runConnector({\n endpoint: resolved.endpoint,\n legacyRoleFlag: args.legacyRole,\n input: process.stdin,\n output: process.stdout,\n errorOutput: process.stderr,\n });\n} catch (error) {\n process.stderr.write(renderError(error));\n process.exitCode = 1;\n}\n"],"mappings":"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","names":["createHash","createHash","isRecord","isRecord","isRecord"]}
1
+ {"version":3,"sources":["../../src/core/config/endpoints.ts","../../src/core/auth/tokenStore.ts","../../src/core/auth/oauth.ts","../../src/core/auth/clientRegistration.ts","../../src/core/harness/legacyRole.ts","../../src/core/version.ts","../../src/core/mcp/rpcClient.ts","../../src/core/mcp/stdioBridge.ts","../../src/adapters/mcp/connector.ts","../../src/core/output/render.ts","../../src/adapters/mcp/args.ts","../../src/bin/mcp.ts"],"sourcesContent":["export type AinectoEnv = \"prod\" | \"dev\";\n\nexport interface EndpointResolutionInput {\n env?: AinectoEnv;\n endpoint?: string;\n envVars?: NodeJS.ProcessEnv;\n}\n\nexport interface ResolvedEndpoint {\n env: AinectoEnv;\n endpoint: string;\n source: \"flag\" | \"env\" | \"default\";\n}\n\n/**\n * 기본 MCP 엔드포인트.\n *\n * OAuth 메타데이터(issuer·resource·authorization_servers)도 ainecto.com 이다.\n */\nconst DEFAULT_ENDPOINTS: Record<AinectoEnv, string> = {\n prod: \"https://ainecto.com/mcp\",\n dev: \"https://dev.ainecto.com/mcp\",\n};\n\nexport function resolveEndpoint(input: EndpointResolutionInput = {}): ResolvedEndpoint {\n const envVars = input.envVars ?? process.env;\n const env = input.env ?? \"prod\";\n\n if (input.endpoint) {\n return { env, endpoint: normalizeEndpoint(input.endpoint), source: \"flag\" };\n }\n\n if (envVars.AINECTO_MCP_ENDPOINT) {\n return { env, endpoint: normalizeEndpoint(envVars.AINECTO_MCP_ENDPOINT), source: \"env\" };\n }\n\n return { env, endpoint: DEFAULT_ENDPOINTS[env], source: \"default\" };\n}\n\nexport function normalizeEndpoint(endpoint: string): string {\n const url = new URL(endpoint);\n assertTrustedEndpointUrl(url, \"MCP endpoint\");\n url.hash = \"\";\n return url.toString().replace(/\\/$/, \"\");\n}\n\n/**\n * ★<b>이 env 의 기본 주소와 «정확히» 같은가.</b>\n *\n * <p>{@link isDefaultEndpoint} 는 prod·dev 를 둘 다 «기본»으로 보는 넓은 판정이라,\n * 「env=prod 인데 endpoint 만 dev」 같은 입력을 기본으로 착각한다. 그 판정으로 설정에서\n * {@code --endpoint} 를 빼면, 조회한 주소와 세션이 붙는 주소가 갈라진다\n * (2026-09-23 독립 재리뷰 I7).\n */\nexport function isDefaultEndpointFor(env: AinectoEnv, endpoint: string): boolean {\n return normalizeEndpoint(endpoint) === DEFAULT_ENDPOINTS[env];\n}\n\nexport function isDefaultEndpoint(endpoint: string): boolean {\n const normalized = normalizeEndpoint(endpoint);\n return Object.values(DEFAULT_ENDPOINTS).includes(normalized);\n}\n\nexport function assertTrustedEndpointUrl(value: string | URL, label = \"URL\"): void {\n const url = typeof value === \"string\" ? new URL(value) : value;\n if (url.protocol === \"https:\") {\n return;\n }\n if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) {\n return;\n }\n throw new Error(`${label} must use https, except http is allowed for localhost loopback endpoints.`);\n}\n\nexport function assertLoopbackRedirectUrl(value: string): void {\n const url = new URL(value);\n if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) {\n return;\n }\n throw new Error(\"OAuth redirect URI must be an http localhost loopback URL.\");\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n return hostname === \"localhost\"\n || hostname === \"127.0.0.1\"\n || hostname === \"::1\"\n || hostname === \"[::1]\";\n}\n\nexport function assertEnv(value: string | undefined): AinectoEnv | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (value === \"prod\" || value === \"dev\") {\n return value;\n }\n throw new Error(`Invalid --env value \"${value}\". Expected \"prod\" or \"dev\".`);\n}\n\n/**\n * 직무 프로필을 고치는 설정 화면 — ★이 MCP 서버와 같은 출처의 홈페이지 설정(기본 탭 = 계정).\n * 규칙은 웹 앱과 같다(ainecto-front {@code src/lib/homepage.ts} 의 {@code /home/settings}, 같은 출처 배포).\n */\nexport function profileSettingsUrl(endpoint: string): string {\n return `${new URL(endpoint).origin}/home/settings`;\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile, chmod } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport interface StoredTokenSet {\n endpoint: string;\n accessToken: string;\n refreshToken?: string;\n tokenType?: string;\n expiresAt?: number;\n scope?: string;\n clientId?: string;\n clientSecret?: string;\n authorizationServer?: string;\n updatedAt: string;\n}\n\n/**\n * 토큰 저장소 — ★서버(endpoint)마다 «한 칸»이다(설계 0.4.2 §2-4). 세션 역할은 없다(0.8.0).\n * 0.4.1 이하가 쓴 역할별 칸은 읽지도 지우지도 않는다(사용자 결정 «마이그레이션은 고려하지 마»).\n */\nexport interface TokenStore {\n load(endpoint: string): Promise<StoredTokenSet | undefined>;\n save(endpoint: string, token: StoredTokenSet): Promise<void>;\n delete(endpoint: string): Promise<void>;\n}\n\ntype TokenFile = Record<string, StoredTokenSet>;\n\nexport class FileTokenStore implements TokenStore {\n readonly filePath: string;\n\n constructor(rootDir = join(homedir(), \".ainecto\")) {\n this.filePath = join(rootDir, \"tokens.json\");\n }\n\n async load(endpoint: string): Promise<StoredTokenSet | undefined> {\n const file = await this.readFile();\n return file[tokenKey(endpoint)];\n }\n\n async save(endpoint: string, token: StoredTokenSet): Promise<void> {\n const file = await this.readFile();\n file[tokenKey(endpoint)] = { ...token, endpoint, updatedAt: new Date().toISOString() };\n await this.writeFile(file);\n }\n\n async delete(endpoint: string): Promise<void> {\n const file = await this.readFile();\n delete file[tokenKey(endpoint)];\n await this.writeFile(file);\n }\n\n private async readFile(): Promise<TokenFile> {\n try {\n await this.hardenExistingPermissions();\n const raw = await readFile(this.filePath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"Token file must contain an object.\");\n }\n return parsed as TokenFile;\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return {};\n }\n throw error;\n }\n }\n\n private async writeFile(file: TokenFile): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true, mode: 0o700 });\n await chmod(dirname(this.filePath), 0o700);\n const tmpPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tmpPath, `${JSON.stringify(file, null, 2)}\\n`, { mode: 0o600 });\n await chmod(tmpPath, 0o600);\n await rename(tmpPath, this.filePath);\n await chmod(this.filePath, 0o600);\n }\n\n private async hardenExistingPermissions(): Promise<void> {\n const dirPath = dirname(this.filePath);\n await chmodIfPermissive(dirPath, 0o700);\n await chmodIfPermissive(this.filePath, 0o600);\n }\n}\n\n/** 서버 하나의 칸. 0.4.1 이하의 «역할 없는» 칸과 같은 키다 — 그때 역할 없이 로그인해 둔 사람은 그대로 쓴다. */\nexport function tokenKey(endpoint: string): string {\n return createHash(\"sha256\").update(endpoint).digest(\"hex\");\n}\n\nexport async function tokenStorePermissions(path: string): Promise<{ mode: number } | undefined> {\n try {\n const result = await stat(path);\n return { mode: result.mode & 0o777 };\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function tokenStoreDirectoryPermissions(path: string): Promise<{ mode: number } | undefined> {\n return tokenStorePermissions(dirname(path));\n}\n\nexport async function removeTokenStoreForTests(path: string): Promise<void> {\n await rm(dirname(path), { recursive: true, force: true });\n}\n\nfunction isNodeError(error: unknown, code: string): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === code;\n}\n\nasync function chmodIfPermissive(path: string, mode: number): Promise<void> {\n try {\n const result = await stat(path);\n if ((result.mode & 0o077) !== 0) {\n await chmod(path, mode);\n }\n } catch (error) {\n if (isNodeError(error, \"ENOENT\")) {\n return;\n }\n throw error;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { createServer } from \"node:http\";\nimport { spawn, type ChildProcess, type SpawnOptions } from \"node:child_process\";\nimport { URLSearchParams } from \"node:url\";\nimport type { StoredTokenSet, TokenStore } from \"./tokenStore\";\nimport { registerPublicClient, type ClientRegistrationResult } from \"./clientRegistration\";\nimport { assertTrustedEndpointUrl, isDefaultEndpoint } from \"../config/endpoints\";\n\n/**\n * ★로그인은 서버마다 한 번이다(설계 0.4.2). 토큰은 역할을 싣지 않고, 역할은 저장소에서 와서\n * 요청 헤더로 실린다({@link McpRpcClient}). 그래서 이 클라이언트에는 역할이 없다.\n */\nexport interface OAuthClientOptions {\n endpoint: string;\n tokenStore: TokenStore;\n fetchImpl?: typeof fetch;\n envVars?: NodeJS.ProcessEnv;\n /**\n * 브라우저를 연다. <b>열기에 실패하면 거절</b>하고, 성공이 확인되면(또는 아직 모르면) 가만히 있는다.\n * login 은 이 약속을 «기다리지 않고» 콜백 대기와 경주시킨다 — 여는 명령이 브라우저가 닫힐 때까지\n * 안 끝나는 환경에서 순서대로 기다리면 교착한다.\n */\n openBrowser?: (url: string) => Promise<void>;\n /**\n * 인가 URL 을 «브라우저를 열기 전에» 알린다. CLI 는 stderr 에 한 줄 쓴다 — 브라우저가 안 열려도\n * 사람이 직접 열 수 있고, 에이전트가 실행했다면 명령이 끝날 때 그 줄이 에이전트에게 닿는다.\n */\n onAuthorizeUrl?: (url: string) => void;\n /** 콜백을 기다리는 한도. 기본 5분 — 사람이 로그인하기엔 넉넉하고, 에이전트 셸 도구 최대 한도보다 짧다. */\n loginTimeoutMs?: number;\n /**\n * 토큰 저장소를 «읽기만» 한다 — 갱신하지 않는다(네트워크도, 토큰 파일 쓰기도 없다).\n * {@code init --dry-run} 이 쓴다: 「아무것도 바꾸지 않는다」에 사용자의 로그인 상태도 든다.\n * 갱신이 필요한(만료된) 토큰은 «없음»으로 본다.\n */\n readOnly?: boolean;\n}\n\n/** 로그인 대기 기본 한도(5분). */\nexport const DEFAULT_LOGIN_TIMEOUT_MS = 300_000;\n\n/**\n * 로그인이 «사람 쪽 사정으로» 끝나지 못한 이유. {@code code} 는 {@code --json} 의 {@code error.code} 로\n * 그대로 나간다(종료 코드는 다른 실패와 같은 1 — 받는 쪽은 메시지를 읽고 사람에게 옮긴다).\n */\nexport class OAuthLoginError extends Error {\n constructor(readonly code: \"LOGIN_TIMEOUT\" | \"BROWSER_UNAVAILABLE\" | \"CALLBACK_UNAVAILABLE\", message: string) {\n super(message);\n this.name = \"OAuthLoginError\";\n }\n}\n\n/**\n * ★저장된 로그인을 인증 서버가 «더는 받지 않는다» — 갱신 요청이 {@code invalid_grant} 등으로 거절됐다\n * (RFC 6749 §5.2: refresh token 만료·폐기, client 등록 폐기). 다시 로그인하는 것 말고는 길이 없다.\n *\n * <p>⚠네트워크 장애·5xx·메타데이터 실패는 이것이 아니다 — 그건 다시 로그인해도 안 고쳐지고,\n * 로그인으로 삼키면 진짜 원인이 브라우저 창 뒤로 숨는다.\n */\nexport class OAuthReauthRequiredError extends Error {\n constructor(readonly oauthError: string, status: number) {\n super(`The stored sign-in was rejected by the server (${oauthError}, HTTP ${status}). Sign in again.`);\n this.name = \"OAuthReauthRequiredError\";\n }\n}\n\n/** 갱신 거절 중 «다시 로그인하라»는 뜻인 OAuth 오류 코드(RFC 6749 §5.2). */\nconst REAUTH_REQUIRED_ERRORS = new Set([\"invalid_grant\", \"invalid_client\", \"unauthorized_client\"]);\n\nexport interface OAuthMetadata {\n authorizationEndpoint: string;\n tokenEndpoint: string;\n registrationEndpoint?: string;\n issuer?: string;\n codeChallengeMethodsSupported: string[];\n}\n\nexport interface TokenProvider {\n getAccessToken(): Promise<string | undefined>;\n refreshAfterUnauthorized(): Promise<string | undefined>;\n}\n\nexport class OAuthClient implements TokenProvider {\n private readonly fetchImpl: typeof fetch;\n private readonly envVars: NodeJS.ProcessEnv;\n private readonly openBrowserImpl: (url: string) => Promise<void>;\n\n constructor(private readonly options: OAuthClientOptions) {\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.envVars = options.envVars ?? process.env;\n this.openBrowserImpl = options.openBrowser ?? openBrowser;\n }\n\n async getAccessToken(): Promise<string | undefined> {\n const envToken = this.envVars.AINECTO_TOKEN;\n if (envToken) {\n assertEnvTokenEndpointAllowed(this.options.endpoint, this.envVars);\n return envToken;\n }\n\n const stored = await this.options.tokenStore.load(this.options.endpoint);\n if (!stored) {\n return undefined;\n }\n\n if (!shouldRefresh(stored)) {\n return stored.accessToken;\n }\n if (this.options.readOnly) {\n return isExpired(stored) ? undefined : stored.accessToken;\n }\n // ★만료됐는데 갱신할 수단이 없으면 «토큰 없음»이다. 예전엔 만료된 토큰을 그대로 돌려줘서,\n // init 은 로그인 대신 401 안내로 떨어졌고 다른 경로는 받을 게 뻔한 401 을 받으러 갔다\n // (2026-09-29 독립 리뷰 P1-2). 아직 60초 창 안이면 쓸 수 있으므로 돌려준다.\n if (!stored.refreshToken) {\n return isExpired(stored) ? undefined : stored.accessToken;\n }\n\n return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });\n }\n\n async refreshAfterUnauthorized(): Promise<string | undefined> {\n const stored = await this.options.tokenStore.load(this.options.endpoint);\n if (this.options.readOnly || !stored?.refreshToken) {\n return undefined;\n }\n return this.refreshStoredToken({ ...stored, refreshToken: stored.refreshToken });\n }\n\n async login(): Promise<StoredTokenSet> {\n const pkce = generatePkcePair();\n const state = generateOAuthState();\n const loopback = await createLoopbackReceiver(state);\n\n try {\n const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);\n const registration = metadata.registrationEndpoint\n ? await registerPublicClient(metadata.registrationEndpoint, loopback.redirectUri, this.fetchImpl)\n : { clientId: \"ainecto-cli\" };\n const authorizeUrl = new URL(metadata.authorizationEndpoint);\n authorizeUrl.searchParams.set(\"response_type\", \"code\");\n authorizeUrl.searchParams.set(\"client_id\", registration.clientId);\n authorizeUrl.searchParams.set(\"redirect_uri\", loopback.redirectUri);\n authorizeUrl.searchParams.set(\"code_challenge\", pkce.codeChallenge);\n authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authorizeUrl.searchParams.set(\"resource\", this.options.endpoint);\n authorizeUrl.searchParams.set(\"state\", state);\n // ★역할을 scope 에 싣지 않는다(설계 0.4.2) — 로그인은 서버마다 한 번이고, 역할은 저장소에서\n // 요청 헤더로 온다. 역할은 작업 가드레일이지 보안 경계가 아니다(2026-09-29 사용자 결정 Q1).\n\n assertTrustedEndpointUrl(authorizeUrl, \"OAuth authorization URL\");\n const url = authorizeUrl.toString();\n this.options.onAuthorizeUrl?.(url);\n // ★셋 중 먼저 오는 것이 결과다 — 콜백 도착 / 브라우저 열기 실패 / 시간 한도.\n // 예전엔 한도가 없고 열기 실패를 못 봐서, 브라우저가 없는 기계에서 말없이 영원히 기다렸다\n // (2026-09-29 독립 리뷰 P1-2). 비TTY(에이전트가 실행)에서는 그 대기가 곧 멈춤이다.\n const browserFailure = this.openBrowserImpl(url).then(() => new Promise<never>(() => undefined));\n const code = await withTimeout(\n Promise.race([loopback.waitForCode(), browserFailure]),\n this.options.loginTimeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS,\n );\n const token = await exchangeToken(metadata.tokenEndpoint, {\n grant_type: \"authorization_code\",\n code,\n redirect_uri: loopback.redirectUri,\n client_id: registration.clientId,\n code_verifier: pkce.codeVerifier,\n resource: this.options.endpoint,\n ...(registration.clientSecret ? { client_secret: registration.clientSecret } : {}),\n }, this.fetchImpl);\n\n const stored = toStoredToken(this.options.endpoint, token, metadata, registration);\n await this.options.tokenStore.save(this.options.endpoint, stored);\n return stored;\n } finally {\n await loopback.close();\n }\n }\n\n async logout(): Promise<void> {\n await this.options.tokenStore.delete(this.options.endpoint);\n }\n\n /** 부르는 쪽이 refresh token 이 있음을 이미 확인했다 — 타입이 그것을 요구한다. */\n private async refreshStoredToken(stored: StoredTokenSet & { refreshToken: string }): Promise<string | undefined> {\n const metadata = await discoverOAuthMetadata(this.options.endpoint, this.fetchImpl);\n const token = await exchangeToken(metadata.tokenEndpoint, {\n grant_type: \"refresh_token\",\n refresh_token: stored.refreshToken,\n client_id: stored.clientId ?? \"ainecto-cli\",\n ...(stored.clientSecret ? { client_secret: stored.clientSecret } : {}),\n resource: this.options.endpoint,\n }, this.fetchImpl);\n const next = toStoredToken(this.options.endpoint, token, metadata, stored);\n await this.options.tokenStore.save(this.options.endpoint, next);\n return next.accessToken;\n }\n}\n\nexport function generatePkcePair(): { codeVerifier: string; codeChallenge: string } {\n const codeVerifier = randomBytes(32).toString(\"base64url\");\n const codeChallenge = createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n return { codeVerifier, codeChallenge };\n}\n\nexport function generateOAuthState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/** 이미 만료됐는가. 만료 시각이 없으면 만료되지 않은 것으로 본다(서버가 수명을 안 알려 준 토큰). */\nexport function isExpired(token: Pick<StoredTokenSet, \"expiresAt\">, now = Date.now()): boolean {\n return token.expiresAt !== undefined && token.expiresAt <= now;\n}\n\nasync function withTimeout<T>(work: Promise<T>, timeoutMs: number): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n const minutes = timeoutMs / 60_000;\n const span = Number.isInteger(minutes) ? `${minutes} minute${minutes === 1 ? \"\" : \"s\"}` : `${timeoutMs} ms`;\n reject(new OAuthLoginError(\n \"LOGIN_TIMEOUT\",\n `Sign-in was not completed within ${span}. Nothing was saved — run the same command again.`,\n ));\n }, timeoutMs);\n });\n try {\n return await Promise.race([work, timeout]);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function shouldRefresh(token: Pick<StoredTokenSet, \"expiresAt\">, now = Date.now()): boolean {\n if (!token.expiresAt) {\n return false;\n }\n return token.expiresAt - now < 60_000;\n}\n\nexport async function discoverOAuthMetadata(endpoint: string, fetchImpl: typeof fetch = fetch): Promise<OAuthMetadata> {\n assertTrustedEndpointUrl(endpoint, \"MCP endpoint\");\n const resourceMetadataUrl = await discoverResourceMetadataUrl(endpoint, fetchImpl);\n assertTrustedEndpointUrl(resourceMetadataUrl, \"OAuth protected resource metadata URL\");\n const resourceMetadata = await fetchJson<Record<string, unknown>>(resourceMetadataUrl, fetchImpl);\n const authorizationServers = resourceMetadata.authorization_servers;\n if (!Array.isArray(authorizationServers) || typeof authorizationServers[0] !== \"string\") {\n throw new Error(\"OAuth protected resource metadata did not include authorization_servers.\");\n }\n const authServer = authorizationServers[0];\n assertTrustedEndpointUrl(authServer, \"OAuth authorization server\");\n const metadataUrl = authServer.includes(\"/.well-known/\")\n ? authServer\n : `${authServer.replace(/\\/$/, \"\")}/.well-known/oauth-authorization-server`;\n assertTrustedEndpointUrl(metadataUrl, \"OAuth authorization server metadata URL\");\n const metadata = await fetchJson<Record<string, unknown>>(metadataUrl, fetchImpl);\n if (typeof metadata.authorization_endpoint !== \"string\" || typeof metadata.token_endpoint !== \"string\") {\n throw new Error(\"OAuth authorization server metadata is missing authorization_endpoint or token_endpoint.\");\n }\n assertTrustedEndpointUrl(metadata.authorization_endpoint, \"OAuth authorization endpoint\");\n assertTrustedEndpointUrl(metadata.token_endpoint, \"OAuth token endpoint\");\n if (typeof metadata.registration_endpoint === \"string\") {\n assertTrustedEndpointUrl(metadata.registration_endpoint, \"OAuth client registration endpoint\");\n }\n if (typeof metadata.issuer === \"string\") {\n assertTrustedEndpointUrl(metadata.issuer, \"OAuth issuer\");\n }\n const codeChallengeMethodsSupported = parseStringArray(metadata.code_challenge_methods_supported);\n if (!codeChallengeMethodsSupported.includes(\"S256\")) {\n throw new Error(\"OAuth authorization server metadata must support PKCE S256.\");\n }\n return {\n authorizationEndpoint: metadata.authorization_endpoint,\n tokenEndpoint: metadata.token_endpoint,\n registrationEndpoint: typeof metadata.registration_endpoint === \"string\" ? metadata.registration_endpoint : undefined,\n issuer: typeof metadata.issuer === \"string\" ? metadata.issuer : undefined,\n codeChallengeMethodsSupported,\n };\n}\n\nasync function discoverResourceMetadataUrl(endpoint: string, fetchImpl: typeof fetch): Promise<string> {\n const challengeUrl = new URL(endpoint);\n assertTrustedEndpointUrl(challengeUrl, \"MCP endpoint\");\n const response = await fetchImpl(challengeUrl, { method: \"GET\" });\n const challenge = response.headers.get(\"www-authenticate\");\n const fromChallenge = challenge ? parseBearerChallengeParam(challenge, \"resource_metadata\") : undefined;\n if (fromChallenge) {\n assertTrustedEndpointUrl(fromChallenge, \"OAuth protected resource metadata URL\");\n return fromChallenge;\n }\n const origin = new URL(endpoint).origin;\n const fallback = `${origin}/.well-known/oauth-protected-resource`;\n assertTrustedEndpointUrl(fallback, \"OAuth protected resource metadata URL\");\n return fallback;\n}\n\nexport function parseBearerChallengeParam(header: string, name: string): string | undefined {\n const pattern = new RegExp(`${name}=\"([^\"]+)\"`);\n return pattern.exec(header)?.[1];\n}\n\nasync function exchangeToken(\n tokenEndpoint: string,\n params: Record<string, string>,\n fetchImpl: typeof fetch,\n): Promise<Record<string, unknown>> {\n assertTrustedEndpointUrl(tokenEndpoint, \"OAuth token endpoint\");\n const response = await fetchImpl(tokenEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(params),\n });\n if (!response.ok) {\n const oauthError = await readOAuthErrorCode(response);\n if (params.grant_type === \"refresh_token\" && oauthError !== undefined && REAUTH_REQUIRED_ERRORS.has(oauthError)) {\n throw new OAuthReauthRequiredError(oauthError, response.status);\n }\n throw new Error(`OAuth token exchange failed with HTTP ${response.status}${oauthError ? ` (${oauthError})` : \"\"}.`);\n }\n return response.json() as Promise<Record<string, unknown>>;\n}\n\n/** 토큰 오류 응답의 {@code error} 코드. 본문이 JSON 이 아니면 없음 — 그때는 «재로그인» 으로 판정하지 않는다. */\nasync function readOAuthErrorCode(response: Response): Promise<string | undefined> {\n try {\n const body = await response.json() as Record<string, unknown>;\n return typeof body.error === \"string\" ? body.error : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * ★<b>쓸 수 있는 저장 로그인 — init·setup 의 단일 진입점.</b> 없으면 undefined(→ 부르는 쪽이 로그인한다).\n *\n * <p>«없음»에는 저장된 갱신 토큰을 서버가 거절한 경우({@link OAuthReauthRequiredError})도 든다 —\n * 예전엔 setup 이 raw {@code getAccessToken} 을 불러, 그 거절이 로그인 «전에» 명령을 끝냈고 같은 setup 을\n * 반복해도 회복하지 않았다(리뷰 #6 F2). 그 밖의 실패(네트워크·불신 주소의 AINECTO_TOKEN 거절)는 그대로 던진다.\n */\nexport async function usableAccessToken(auth: Pick<TokenProvider, \"getAccessToken\">): Promise<string | undefined> {\n try {\n return await auth.getAccessToken();\n } catch (error) {\n if (error instanceof OAuthReauthRequiredError) {\n return undefined;\n }\n throw error;\n }\n}\n\ntype TokenContext = Partial<Pick<StoredTokenSet, \"refreshToken\" | \"tokenType\" | \"expiresAt\" | \"scope\" | \"clientId\" | \"clientSecret\">>\n & Partial<ClientRegistrationResult>;\n\nfunction toStoredToken(\n endpoint: string,\n token: Record<string, unknown>,\n metadata: OAuthMetadata,\n previous?: TokenContext,\n): StoredTokenSet {\n if (typeof token.access_token !== \"string\") {\n throw new Error(\"OAuth token response did not include access_token.\");\n }\n const expiresIn = typeof token.expires_in === \"number\" ? token.expires_in : undefined;\n return {\n endpoint,\n accessToken: token.access_token,\n refreshToken: typeof token.refresh_token === \"string\" ? token.refresh_token : previous?.refreshToken,\n tokenType: typeof token.token_type === \"string\" ? token.token_type : previous?.tokenType,\n expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : previous?.expiresAt,\n scope: typeof token.scope === \"string\" ? token.scope : previous?.scope,\n clientId: previous?.clientId ?? \"ainecto-cli\",\n clientSecret: previous?.clientSecret,\n authorizationServer: metadata.issuer,\n updatedAt: new Date().toISOString(),\n };\n}\n\n/**\n * 로그인 콜백을 받는 loopback 서버. {@code host} 는 시험이 «바인딩 실패»를 만들 때만 바꾼다.\n */\nexport async function createLoopbackReceiver(expectedState: string, host = \"127.0.0.1\"): Promise<{\n redirectUri: string;\n waitForCode(): Promise<string>;\n close(): Promise<void>;\n}> {\n let resolveCode!: (code: string) => void;\n let rejectCode!: (error: Error) => void;\n const codePromise = new Promise<string>((resolve, reject) => {\n resolveCode = resolve;\n rejectCode = reject;\n });\n codePromise.catch(() => undefined);\n\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n const error = url.searchParams.get(\"error\");\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n if (error) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization failed. You can close this tab.\");\n rejectCode(new Error(`OAuth authorization failed: ${error}`));\n return;\n }\n if (!state || state !== expectedState) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization state mismatch. You can close this tab.\");\n rejectCode(new Error(\"OAuth state mismatch.\"));\n return;\n }\n if (!code) {\n res.writeHead(400, { \"content-type\": \"text/plain\" });\n res.end(\"Missing OAuth code.\");\n rejectCode(new Error(\"OAuth redirect did not include a code.\"));\n return;\n }\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"AiNecto CLI authorization complete. You can close this tab.\");\n resolveCode(code);\n });\n\n // ★바인딩이 막히면(에이전트 sandbox 등) 예전엔 error 리스너가 없어 처리되지 않은 예외로 죽었다\n // (2026-09-29 코드 리뷰 P1). 사람이 할 일을 말하는 오류로 바꾼다.\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", (error) => reject(new OAuthLoginError(\n \"CALLBACK_UNAVAILABLE\",\n `Could not start the local sign-in callback on ${host} (${error.message}). `\n + \"If this command runs in a sandbox, allow local network binding or run it outside the sandbox.\",\n )));\n server.listen(0, host, () => resolve());\n });\n const address = server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"Loopback OAuth server did not bind to a TCP port.\");\n }\n return {\n redirectUri: `http://127.0.0.1:${address.port}/callback`,\n waitForCode: () => codePromise,\n close: () => new Promise<void>((resolve, reject) => {\n // Node 18 은 브라우저가 붙들고 있는 keep-alive 연결이 끝날 때까지 close 가 늦어진다.\n server.closeAllConnections?.();\n server.close((error) => error ? reject(error) : resolve());\n }),\n };\n}\n\nfunction parseStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n}\n\nasync function fetchJson<T>(url: string, fetchImpl: typeof fetch): Promise<T> {\n assertTrustedEndpointUrl(url, \"OAuth metadata URL\");\n const response = await fetchImpl(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch OAuth metadata from ${url}: HTTP ${response.status}.`);\n }\n return response.json() as Promise<T>;\n}\n\nexport function getBrowserOpenCommand(\n url: string,\n platform: NodeJS.Platform = process.platform,\n): { command: string; args: string[] } {\n assertTrustedEndpointUrl(url, \"OAuth authorization URL\");\n if (platform === \"darwin\") {\n return { command: \"open\", args: [url] };\n }\n if (platform === \"win32\") {\n return { command: \"explorer.exe\", args: [url] };\n }\n return { command: \"xdg-open\", args: [url] };\n}\n\nexport interface LaunchBrowserDeps {\n platform?: NodeJS.Platform;\n spawnImpl?: (command: string, args: string[], options: SpawnOptions) => ChildProcess;\n}\n\n/**\n * 기본 브라우저 열기. <b>여는 명령이 없거나(ENOENT) 0 이 아닌 코드로 끝나면 거절</b>한다.\n *\n * <p>★예전엔 {@code error} 리스너가 없어 명령이 없으면 처리되지 않은 오류로 죽었고, 명령은 있는데\n * 브라우저가 없으면({@code xdg-open} 비0 종료) 아무 말 없이 콜백을 영원히 기다렸다\n * (2026-09-29 독립 리뷰 P1-2).\n *\n * <p>⚠<b>Windows 는 종료 코드를 보지 않는다.</b> {@code explorer.exe} 는 성공해도 1 로 끝나는 것으로\n * 알려져 있어, 그 코드로 판정하면 정상 환경을 실패로 만든다 — 명령이 «떴다»는 것만 본다\n * (설계 §8-1, Windows 실측 V14 전까지 추정).\n */\nexport function launchBrowser(url: string, deps: LaunchBrowserDeps = {}): Promise<void> {\n const platform = deps.platform ?? process.platform;\n const { command, args } = getBrowserOpenCommand(url, platform);\n const spawnImpl = deps.spawnImpl ?? spawn;\n return new Promise<void>((resolve, reject) => {\n const unavailable = (reason: string) => new OAuthLoginError(\n \"BROWSER_UNAVAILABLE\",\n `Could not open a browser on this machine (${command}: ${reason}). Sign-in needs a browser here.`,\n );\n let child: ChildProcess;\n try {\n child = spawnImpl(command, args, { stdio: \"ignore\", detached: true });\n } catch (error) {\n reject(unavailable(error instanceof Error ? error.message : String(error)));\n return;\n }\n child.once(\"error\", (error) => reject(unavailable(error.message)));\n if (platform === \"win32\") {\n child.once(\"spawn\", () => resolve());\n } else {\n child.once(\"exit\", (code, signal) => {\n if (code === 0) {\n resolve();\n } else {\n reject(unavailable(code === null ? `stopped by ${signal ?? \"a signal\"}` : `exited with code ${code}`));\n }\n });\n }\n child.unref();\n });\n}\n\nfunction openBrowser(url: string): Promise<void> {\n return launchBrowser(url);\n}\n\nfunction assertEnvTokenEndpointAllowed(endpoint: string, envVars: NodeJS.ProcessEnv): void {\n if (envVars.AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN === \"1\") {\n return;\n }\n if (isDefaultEndpoint(endpoint)) {\n return;\n }\n throw new Error(\"AINECTO_TOKEN can only be used with the default prod/dev endpoints. Set AINECTO_ALLOW_CUSTOM_ENDPOINT_TOKEN=1 only when you intentionally trust the custom endpoint.\");\n}\n","import { assertLoopbackRedirectUrl, assertTrustedEndpointUrl } from \"../config/endpoints\";\n\nexport interface ClientRegistrationResult {\n clientId: string;\n clientSecret?: string;\n}\n\nexport async function registerPublicClient(\n registrationEndpoint: string,\n redirectUri: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<ClientRegistrationResult> {\n assertTrustedEndpointUrl(registrationEndpoint, \"OAuth client registration endpoint\");\n assertLoopbackRedirectUrl(redirectUri);\n const response = await fetchImpl(registrationEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", accept: \"application/json\" },\n body: JSON.stringify({\n client_name: \"AiNecto CLI\",\n redirect_uris: [redirectUri],\n token_endpoint_auth_method: \"none\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n }),\n });\n if (!response.ok) {\n throw new Error(`OAuth client registration failed with HTTP ${response.status}.`);\n }\n const body = await response.json() as unknown;\n if (!isRecord(body) || typeof body.client_id !== \"string\") {\n throw new Error(\"OAuth client registration response did not include client_id.\");\n }\n return {\n clientId: body.client_id,\n clientSecret: typeof body.client_secret === \"string\" ? body.client_secret : undefined,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * 옛 세션 역할 입력 — ★0.8.0 에서 세션 역할(Design/Development/Test/Validation)이 없어졌다.\n * 도구 목록은 모든 세션이 같고, 무엇을 할 수 있는지는 서버가 사용자·리소스 권한으로만 정한다.\n *\n * <p>그래도 옛 입력은 «거절하지 않는다» — 0.7.x 의 {@code init} 이 쓴 저장소 설정\n * ({@code npx -y @ainecto/mcp --role design}, 버전 고정 없음)은 게시 즉시 이 판을 띄운다.\n * 여기서 {@code --role} 을 거절하면 그 저장소 전부의 연결이 끊긴다(설계 §2-3 R1).\n *\n * <p>받은 값은 «검사하지 않고» 두 군데에만 쓴다:\n * <ul>\n * <li>stderr 안내 한 줄({@link LEGACY_ROLE_NOTICE}) — 사람이 설정을 정리하게.</li>\n * <li>같은 헤더 {@link LEGACY_ROLE_HEADER} 로 서버에 실어 보냄 — 서버는 판정에 쓰지 않고\n * {@code [mcp][legacy-role]} 로 기록만 한다. 새 헤더를 만들지 않으려고 옛 이름을 재사용한다.</li>\n * </ul>\n * ★관찰 기간(설계 §7-2 ⑤) 뒤 기록이 0건이면 이 파일과 브리지의 {@code --role} 받기를 함께 지운다.\n */\n\n/** 서버가 기록용으로 읽는 헤더 — 서버 McpController 의 옛 역할 헤더와 같은 이름. */\nexport const LEGACY_ROLE_HEADER = \"X-AiNecto-Role\";\n\n/** 0.7.x 가 Codex 처럼 전역 설정뿐인 에이전트에 안내하던 환경변수. */\nexport const LEGACY_ROLE_ENV_VAR = \"AINECTO_ROLE\";\n\nexport const LEGACY_ROLE_NOTICE =\n \"AiNecto session roles were removed; access follows your AiNecto permissions. \"\n + \"Re-run `ainecto init` to tidy this repository.\";\n\n/**\n * 플래그(있으면) 아니면 {@link LEGACY_ROLE_ENV_VAR}. ★값을 검사하지 않는다 — 모르는 값도 받는다.\n * 빈 값은 «없음»이다.\n */\nexport function legacyRoleInput(flag: string | undefined, envVars: NodeJS.ProcessEnv = process.env): string | undefined {\n const value = flag ?? envVars[LEGACY_ROLE_ENV_VAR];\n return value !== undefined && value.trim() ? value.trim() : undefined;\n}\n","/**\n * CLI 버전. ★진실원은 {@code package.json} 이고 빌드(tsup define)가 그 값을 박는다 —\n * 상수로 또 적으면 릴리스 때 한쪽만 올라가 조용히 거짓이 된다.\n * 번들을 거치지 않는 실행(vitest·tsx)에서는 값이 없으므로 \"dev\" 로 떨어진다.\n */\ndeclare const __AINECTO_CLI_VERSION__: string | undefined;\n\n/** 번들을 거치지 않은 실행의 판 표시. */\nconst UNBUNDLED_VERSION = \"dev\";\n\nexport const CLI_VERSION: string =\n typeof __AINECTO_CLI_VERSION__ === \"string\" ? __AINECTO_CLI_VERSION__ : UNBUNDLED_VERSION;\n\n/**\n * npm 주 이름. ★이 이름을 박는 곳은 여기 하나다 — init·setup 이 쓰는 {@code npx -y <이름>@<판>},\n * MCP {@code clientInfo}, User-Agent, init 기록의 {@code generatedBy} 가 모두 이것을 쓴다.\n * {@code package.json} 의 name·{@code server.json} 의 패키지 식별자와 같아야 한다(시험이 대조한다).\n *\n * <p>다른 이름 {@code ainecto}·{@code @ainecto/cli} 에도 같은 코드가 올라가지만(scripts/publish.mjs),\n * 그 판으로 init 해도 이 이름을 쓴다.\n */\nexport const PACKAGE_NAME = \"@ainecto/mcp\";\n\nexport const CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;\n\n/**\n * MCP 설정에 쓰는 브리지 패키지 지정 — ★이 CLI 판으로 «고정»한다(설계 결정 #6·D8). 고정하지 않으면\n * 게시 즉시 모든 저장소의 브리지가 새 판으로 바뀐다(0.7.x 의 --role 설정이 그랬다). 올리는 길은\n * {@code setup}/{@code init} 재실행이다. 판을 모르는 실행(vitest·tsx)에서는 고정하지 않는다 —\n * {@code @dev} 는 npm 에서 판이 아니라 배포 태그로 읽힌다.\n */\nexport function bridgePackageSpec(version: string = CLI_VERSION): string {\n return version === UNBUNDLED_VERSION ? PACKAGE_NAME : `${PACKAGE_NAME}@${version}`;\n}\n","import type { TokenProvider } from \"../auth/oauth\";\nimport { LEGACY_ROLE_HEADER } from \"../harness/legacyRole\";\nimport { CLI_VERSION, PACKAGE_NAME } from \"../version\";\n\nexport interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcSuccess {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n result: unknown;\n}\n\nexport interface JsonRpcFailure {\n jsonrpc: \"2.0\";\n id?: string | number | null;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport type JsonRpcResponse = JsonRpcSuccess | JsonRpcFailure;\n\n/**\n * 분석용 요청 헤더(GA4 서버 계측 C23). 서버의 ExecutionChannel.HEADER · AgentTypeResolver.HEADER 와 같아야 한다.\n * 서버는 /mcp 필터에서 한 번만 읽고, 모르는 값이면 요청을 막지 않는다(분석 값일 뿐이다).\n */\nexport const CHANNEL_HEADER = \"X-AiNecto-Channel\";\nexport const AGENT_HEADER = \"X-AiNecto-Agent\";\n/** 서버 AgentTypeResolver.MAX_NAME_LENGTH 와 같다. */\nexport const MAX_AGENT_NAME_LENGTH = 100;\n\n/** 이 클라이언트가 서버에 밝히는 길 — 직접 명령(`ainecto tools call …`) = cli, 에이전트의 MCP 브리지 = mcp. */\nexport type ExecutionChannel = \"cli\" | \"mcp\";\n\nexport interface McpRpcClientOptions {\n endpoint: string;\n tokenProvider?: TokenProvider;\n fetchImpl?: typeof fetch;\n envVars?: NodeJS.ProcessEnv;\n /**\n * 옛 {@code --role}/{@code AINECTO_ROLE} 값 — ★기록용이다(legacyRole.ts). 있으면 모든 요청의\n * {@code X-AiNecto-Role} 헤더로 실리고, 서버는 판정에 쓰지 않고 로그로만 남긴다.\n */\n legacyRole?: string;\n /**\n * 요청의 길. ★필수다 — 빠뜨리면 서버가 직접 명령을 MCP 로 센다. 그 빠뜨림을 타입이 잡는다.\n * 모든 요청의 {@code X-AiNecto-Channel} 헤더로 실린다.\n */\n channel: ExecutionChannel;\n}\n\nexport class McpRpcError extends Error {\n constructor(\n message: string,\n readonly code: string,\n readonly details?: unknown,\n ) {\n super(message);\n }\n}\n\nexport class McpRpcClient {\n private readonly fetchImpl: typeof fetch;\n private nextId = 1;\n private agent: string | undefined;\n\n constructor(private readonly options: McpRpcClientOptions) {\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n /**\n * 브리지 전용 — 호스트 에이전트가 보낸 {@code initialize} 의 {@code clientInfo.name}(프로그램 고정 이름).\n * 이후 모든 요청의 {@code X-AiNecto-Agent} 헤더로 싣는다. 직접 명령은 부르지 않는다(누가 셸을 돌렸는지 모른다).\n */\n useAgent(clientName: unknown): void {\n this.agent = agentHeaderValue(clientName);\n }\n\n initialize(): Promise<unknown> {\n return this.request(\"initialize\", {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION },\n });\n }\n\n async toolsList(): Promise<unknown[]> {\n const result = await this.request(\"tools/list\", {});\n if (!isRecord(result) || !Array.isArray(result.tools)) {\n throw new McpRpcError(\"tools/list response did not include a tools array.\", \"MCP_PROTOCOL_ERROR\", result);\n }\n return result.tools;\n }\n\n async toolsCall(name: string, args: unknown): Promise<unknown> {\n return this.request(\"tools/call\", { name, arguments: args ?? {} });\n }\n\n async request(method: string, params?: unknown): Promise<unknown> {\n const response = await this.sendRaw({\n jsonrpc: \"2.0\",\n id: this.nextId++,\n method,\n params,\n });\n if (!response) {\n return undefined;\n }\n if (\"error\" in response) {\n throw new McpRpcError(response.error.message, \"MCP_ERROR\", response.error);\n }\n return response.result;\n }\n\n async sendRaw(request: JsonRpcRequest): Promise<JsonRpcResponse | undefined> {\n const token = await this.options.tokenProvider?.getAccessToken();\n let response = await this.postJson(request, token);\n if (response.status === 401 && this.options.tokenProvider) {\n const refreshed = await this.options.tokenProvider.refreshAfterUnauthorized();\n if (refreshed) {\n response = await this.postJson(request, refreshed);\n }\n }\n\n if (response.status === 204 || response.status === 202) {\n return undefined;\n }\n const text = await response.text();\n if (!response.ok) {\n throw new McpRpcError(\n `MCP HTTP request failed with HTTP ${response.status}.`,\n \"MCP_HTTP_ERROR\",\n httpErrorDetails(response.status, text, this.options.envVars ?? process.env),\n );\n }\n if (!text.trim()) {\n return undefined;\n }\n const parsed = JSON.parse(text) as unknown;\n if (!isJsonRpcResponse(parsed)) {\n throw new McpRpcError(\"MCP response was not a JSON-RPC response.\", \"MCP_PROTOCOL_ERROR\", parsed);\n }\n return parsed;\n }\n\n private postJson(request: JsonRpcRequest, token: string | undefined): Promise<Response> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n };\n if (token) {\n headers.authorization = `Bearer ${token}`;\n }\n const legacyRole = printableHeaderValue(this.options.legacyRole);\n if (legacyRole) {\n headers[LEGACY_ROLE_HEADER.toLowerCase()] = legacyRole;\n }\n headers[CHANNEL_HEADER.toLowerCase()] = this.options.channel;\n if (this.agent) {\n headers[AGENT_HEADER.toLowerCase()] = this.agent;\n }\n return this.fetchImpl(this.options.endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify(request),\n });\n }\n}\n\n/**\n * ★MCP 는 «도구가 거부/실패했다»를 JSON-RPC error 가 아니라 결과 안의 {@code isError} 로\n * 알린다(2025-06-18 규격). 그래서 최상위 error 만 보면 실패가 성공으로 보고된다 —\n * 셸 자동화는 exit 0 을 받고 다음 단계로 간다(2026-09-22 독립 리뷰 I9).\n */\nexport function isToolError(result: unknown): boolean {\n return isRecord(result) && result.isError === true;\n}\n\n/**\n * 헤더에 실을 수 있는 이름 — 인쇄 가능한 ASCII 만(그 밖의 글자는 fetch 가 헤더로 거절한다), 앞뒤 공백 제거,\n * 최대 {@link MAX_AGENT_NAME_LENGTH}자. 남는 것이 없으면 싣지 않는다(서버는 unknown 으로 센다).\n */\nexport function agentHeaderValue(clientName: unknown): string | undefined {\n return printableHeaderValue(clientName);\n}\n\n/** 헤더에 실을 수 있는 모양으로 — 규칙은 {@link agentHeaderValue} 와 같다(이 파일의 헤더 값은 전부 이 길을 지난다). */\nfunction printableHeaderValue(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const printable = value.replace(/[^\\x20-\\x7E]/g, \"\").trim().slice(0, MAX_AGENT_NAME_LENGTH).trim();\n return printable || undefined;\n}\n\nfunction isJsonRpcResponse(value: unknown): value is JsonRpcResponse {\n return isRecord(value)\n && value.jsonrpc === \"2.0\"\n && (\"result\" in value || \"error\" in value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction httpErrorDetails(status: number, body: string, envVars: NodeJS.ProcessEnv): Record<string, unknown> {\n const details: Record<string, unknown> = { status };\n if (envVars.AINECTO_CLI_DEBUG === \"1\" || envVars.AINECTO_DEBUG === \"1\") {\n details.body = safeBodyPreview(body);\n }\n return details;\n}\n\nexport function safeBodyPreview(body: string): string {\n return body.length > 500 ? `${body.slice(0, 500)}...` : body;\n}\n","import { createInterface } from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { McpRpcClient, type JsonRpcRequest } from \"./rpcClient\";\n\nexport interface StdioBridgeOptions {\n input: Readable;\n output: Writable;\n errorOutput: Writable;\n client: McpRpcClient;\n}\n\nexport async function runStdioBridge(options: StdioBridgeOptions): Promise<void> {\n const lines = createInterface({ input: options.input, crlfDelay: Infinity });\n\n for await (const line of lines) {\n if (!line.trim()) {\n continue;\n }\n try {\n const request = parseJsonRpcRequest(line);\n if (request.method === \"initialize\") {\n // 호스트 에이전트의 고정 이름 — 이 세션의 모든 요청에 X-AiNecto-Agent 로 실린다(initialize 자신 포함).\n options.client.useAgent(clientInfoName(request.params));\n }\n const response = await options.client.sendRaw(request);\n if (response && request.id !== undefined) {\n options.output.write(`${JSON.stringify(response)}\\n`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n options.output.write(`${JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32603, message },\n })}\\n`);\n }\n }\n}\n\nfunction parseJsonRpcRequest(line: string): JsonRpcRequest {\n const parsed = JSON.parse(line) as unknown;\n if (!isRecord(parsed) || parsed.jsonrpc !== \"2.0\" || typeof parsed.method !== \"string\") {\n throw new Error(\"Invalid JSON-RPC request.\");\n }\n return {\n jsonrpc: \"2.0\",\n id: typeof parsed.id === \"string\" || typeof parsed.id === \"number\" || parsed.id === null ? parsed.id : undefined,\n method: parsed.method,\n params: parsed.params,\n };\n}\n\nfunction clientInfoName(params: unknown): unknown {\n return isRecord(params) && isRecord(params.clientInfo) ? params.clientInfo.name : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { FileTokenStore } from \"../../core/auth/tokenStore\";\nimport { OAuthClient } from \"../../core/auth/oauth\";\nimport { McpRpcClient } from \"../../core/mcp/rpcClient\";\nimport { runStdioBridge } from \"../../core/mcp/stdioBridge\";\nimport { LEGACY_ROLE_NOTICE, legacyRoleInput } from \"../../core/harness/legacyRole\";\nimport type { Readable, Writable } from \"node:stream\";\n\nexport interface ConnectorOptions {\n endpoint: string;\n /** 옛 MCP 설정의 {@code --role} 값(검사하지 않는다). 없으면 {@code AINECTO_ROLE} 를 본다 — 기록용. */\n legacyRoleFlag?: string;\n envVars?: NodeJS.ProcessEnv;\n input: Readable;\n output: Writable;\n errorOutput: Writable;\n}\n\nexport async function runConnector(options: ConnectorOptions): Promise<void> {\n // ★토큰은 서버마다 한 칸이다. 세션 역할은 없다 — 도구 목록·권한은 서버가 사용자 권한으로 정한다.\n const legacyRole = legacyRoleInput(options.legacyRoleFlag, options.envVars);\n if (legacyRole !== undefined) {\n // stdout 은 JSON-RPC 전용이다 — 안내는 stderr 로만.\n options.errorOutput.write(`${LEGACY_ROLE_NOTICE}\\n`);\n }\n const auth = new OAuthClient({ endpoint: options.endpoint, tokenStore: new FileTokenStore() });\n const client = new McpRpcClient({\n endpoint: options.endpoint,\n tokenProvider: auth,\n legacyRole,\n // 에이전트의 MCP 연결 — 에이전트 이름은 브리지가 호스트의 initialize 에서 읽어 싣는다.\n channel: \"mcp\",\n });\n await runStdioBridge({\n input: options.input,\n output: options.output,\n errorOutput: options.errorOutput,\n client,\n });\n}\n","export interface CliErrorShape {\n ok: false;\n error: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\nexport interface CliSuccessShape {\n ok: true;\n data: unknown;\n warnings?: string[];\n}\n\nexport function renderSuccess(data: unknown, options: { json?: boolean; warnings?: string[] } = {}): string {\n if (options.json) {\n const shape: CliSuccessShape = { ok: true, data };\n if (options.warnings?.length) {\n shape.warnings = options.warnings;\n }\n return `${JSON.stringify(shape, null, 2)}\\n`;\n }\n const warnings = options.warnings?.length ? `${options.warnings.map((warning) => `Warning: ${warning}`).join(\"\\n\")}\\n` : \"\";\n return `${warnings}${renderHuman(data)}\\n`;\n}\n\nexport function renderError(error: unknown, options: { json?: boolean } = {}): string {\n const shape = toErrorShape(error);\n if (options.json) {\n return `${JSON.stringify(shape, null, 2)}\\n`;\n }\n return `Error: ${shape.error.message}\\n`;\n}\n\nexport function toErrorShape(error: unknown): CliErrorShape {\n if (error instanceof Error) {\n const maybe = error as Error & { code?: string; details?: unknown };\n return {\n ok: false,\n error: {\n code: maybe.code ?? \"CLI_ERROR\",\n message: error.message,\n details: redactSecrets(maybe.details),\n },\n };\n }\n return { ok: false, error: { code: \"CLI_ERROR\", message: String(error) } };\n}\n\nexport function redactSecrets(value: unknown): unknown {\n return redactSecretsInner(value, new WeakSet<object>());\n}\n\nfunction renderHuman(data: unknown): string {\n if (Array.isArray(data)) {\n return data.map((item) => renderHuman(item)).join(\"\\n\");\n }\n if (typeof data === \"string\") {\n return data;\n }\n return JSON.stringify(data, null, 2);\n}\n\nfunction redactSecretsInner(value: unknown, seen: WeakSet<object>): unknown {\n if (typeof value === \"string\") {\n return redactSecretString(value);\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactSecretsInner(item, seen));\n }\n if (value && typeof value === \"object\") {\n if (seen.has(value)) {\n return \"[circular]\";\n }\n seen.add(value);\n const output: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n output[key] = isSecretKey(key) ? \"[redacted]\" : redactSecretsInner(child, seen);\n }\n return output;\n }\n return value;\n}\n\nfunction isSecretKey(key: string): boolean {\n const normalized = key.toLowerCase().replace(/-/g, \"_\");\n return normalized === \"authorization\"\n || normalized.includes(\"access_token\")\n || normalized.includes(\"refresh_token\")\n || normalized.includes(\"client_secret\")\n || normalized.includes(\"token\");\n}\n\nfunction redactSecretString(value: string): string {\n return value\n .replace(/(\"(?:authorization|access_token|refresh_token|client_secret|token)\"\\s*:\\s*\")([^\"]*)(\")/gi, \"$1[redacted]$3\")\n .replace(/((?:authorization|access_token|refresh_token|client_secret|token)=)([^&\\s\"']+)/gi, \"$1[redacted]\")\n .replace(/(authorization:\\s*bearer\\s+)([^\\s,}]+)/gi, \"$1[redacted]\")\n .replace(/(bearer\\s+)(eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+)/gi, \"$1[redacted]\");\n}\n","import { assertEnv, type AinectoEnv } from \"../../core/config/endpoints\";\n\nexport interface McpBinArgs {\n env?: AinectoEnv;\n endpoint?: string;\n /**\n * 옛 {@code --role} 값 — ★검사하지 않는다(legacyRole.ts). 0.7.x 가 쓴 저장소 설정이 이 판을 띄우므로\n * 여기서 멈추면 그 연결이 전부 끊긴다. 값 없이 끝난 {@code --role} 도 멈추지 않고 «없음»으로 둔다.\n */\n legacyRole?: string;\n help: boolean;\n}\n\nexport function parseMcpArgs(argv: string[]): McpBinArgs {\n const result: McpBinArgs = { help: false };\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (!arg) {\n continue;\n }\n if (arg === \"--env\") {\n result.env = assertEnv(requireFlagValue(arg, argv[++index]));\n } else if (arg === \"--endpoint\") {\n result.endpoint = requireFlagValue(arg, argv[++index]);\n } else if (arg === \"--role\") {\n const value = argv[index + 1];\n if (value !== undefined && !value.startsWith(\"--\")) {\n result.legacyRole = value;\n index += 1;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n result.help = true;\n } else {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n return result;\n}\n\nfunction requireFlagValue(flag: string, value: string | undefined): string {\n if (!value || value.startsWith(\"--\")) {\n throw new Error(`${flag} requires a value.`);\n }\n return value;\n}\n","#!/usr/bin/env node\nimport { resolveEndpoint } from \"../core/config/endpoints\";\nimport { runConnector } from \"../adapters/mcp/connector\";\nimport { renderError } from \"../core/output/render\";\nimport { parseMcpArgs } from \"../adapters/mcp/args\";\n\ntry {\n const args = parseMcpArgs(process.argv.slice(2));\n if (args.help) {\n process.stdout.write(\"Usage: mcp [--env prod|dev] [--endpoint URL]\\n\");\n process.exit(0);\n }\n const resolved = resolveEndpoint({ env: args.env, endpoint: args.endpoint });\n await runConnector({\n endpoint: resolved.endpoint,\n legacyRoleFlag: args.legacyRole,\n input: process.stdin,\n output: process.stdout,\n errorOutput: process.stderr,\n });\n} catch (error) {\n process.stderr.write(renderError(error));\n process.exitCode = 1;\n}\n"],"mappings":"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","names":["createHash","createHash","isRecord","isRecord","isRecord"]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ainecto/cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "AiNecto CLI — connect your coding agents to AiNecto once per machine: npx -y ainecto@latest setup. Then npx -y ainecto@latest doctor shows anything missing.",
5
- "mcpName": "io.github.codelives/ainecto",
5
+ "mcpName": "com.ainecto/mcp",
6
6
  "type": "module",
7
7
  "private": false,
8
8
  "license": "UNLICENSED",