@oscharko-dev/keiko 0.1.4 → 0.1.6

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.
@@ -2,6 +2,7 @@ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, write
2
2
  import { spawn } from "node:child_process";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { SDK_VERSION } from "../sdk/index.js";
5
6
  import { DEFAULT_UI_PORT, UI_HOST } from "../ui/index.js";
6
7
  const ALLOWED_HOSTS = new Set(["127.0.0.1", "localhost"]);
7
8
  const LIFECYCLE_FLAG_SETTERS = {
@@ -106,6 +107,33 @@ function logFile(options) {
106
107
  function healthUrl(options) {
107
108
  return `http://${options.host}:${String(options.port)}/api/health`;
108
109
  }
110
+ function healthVersion(payload) {
111
+ if (typeof payload !== "object" || payload === null)
112
+ return undefined;
113
+ const version = payload.version;
114
+ return typeof version === "string" ? version : undefined;
115
+ }
116
+ async function probeHealth(options, fetchImpl) {
117
+ try {
118
+ const response = await fetchImpl(healthUrl(options), {
119
+ signal: AbortSignal.timeout(1_000),
120
+ });
121
+ if (!response.ok) {
122
+ return { reachable: false, version: undefined };
123
+ }
124
+ let body;
125
+ try {
126
+ body = await response.json();
127
+ }
128
+ catch {
129
+ return { reachable: true, version: undefined };
130
+ }
131
+ return { reachable: true, version: healthVersion(body) };
132
+ }
133
+ catch {
134
+ return { reachable: false, version: undefined };
135
+ }
136
+ }
109
137
  function readPid(path) {
110
138
  if (!existsSync(path))
111
139
  return undefined;
@@ -195,8 +223,20 @@ async function waitForHealth(options, pid, deps) {
195
223
  async function cmdStart(options, io, env, deps, cwd) {
196
224
  const running = runningPid(options, deps.isProcessAlive);
197
225
  if (running !== undefined) {
198
- io.out(`Keiko UI already running on ${healthUrl(options).replace("/api/health", "")} (pid ${String(running)}).\n`);
199
- return 0;
226
+ const health = await probeHealth(options, deps.fetchImpl);
227
+ if (health.version === SDK_VERSION) {
228
+ io.out(`Keiko UI already running on ${healthUrl(options).replace("/api/health", "")} (pid ${String(running)}).\n`);
229
+ return 0;
230
+ }
231
+ const reason = !health.reachable
232
+ ? "health check is unreachable"
233
+ : health.version === undefined
234
+ ? "health check did not return the current Keiko version"
235
+ : `running version ${health.version} differs from installed version ${SDK_VERSION}`;
236
+ io.out(`Keiko UI process is stale (${reason}); restarting pid ${String(running)}.\n`);
237
+ const stopped = await cmdStop(options, io, deps);
238
+ if (stopped !== 0)
239
+ return stopped;
200
240
  }
201
241
  const { child, logPath } = spawnUiProcess(options, env, deps, cwd);
202
242
  if (child.pid === undefined) {
@@ -4,5 +4,7 @@ export interface GatewayFetchOptions extends RequestInit {
4
4
  readonly useCaFallback?: boolean | undefined;
5
5
  }
6
6
  export declare function isMissingIssuerError(error: unknown): boolean;
7
+ export declare function isRecoverableTlsTrustError(error: unknown): boolean;
8
+ export declare function gatewayTrustedCaCertificates(): readonly string[];
7
9
  export declare function gatewayFetch(url: string, options?: GatewayFetchOptions): Promise<Response>;
8
10
  export declare function readJsonCapped(response: Response, maxBytes?: number): Promise<unknown>;
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { request as httpsRequest } from "node:https";
3
- import { rootCertificates } from "node:tls";
3
+ import * as tls from "node:tls";
4
4
  // Caps a single gateway response at 10 MB; real chat completions are far smaller.
5
5
  export const MAX_RESPONSE_BYTES = 10_000_000;
6
6
  function headersFromNode(headers) {
@@ -36,6 +36,21 @@ export function isMissingIssuerError(error) {
36
36
  return item.code === "UNABLE_TO_GET_ISSUER_CERT_LOCALLY";
37
37
  });
38
38
  }
39
+ const RECOVERABLE_TLS_TRUST_ERROR_CODES = new Set([
40
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
41
+ "SELF_SIGNED_CERT_IN_CHAIN",
42
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
43
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
44
+ ]);
45
+ export function isRecoverableTlsTrustError(error) {
46
+ const cause = isRecord(error) ? error.cause : undefined;
47
+ const candidates = [error, cause];
48
+ return candidates.some((item) => {
49
+ if (!isRecord(item) || typeof item.code !== "string")
50
+ return false;
51
+ return RECOVERABLE_TLS_TRUST_ERROR_CODES.has(item.code);
52
+ });
53
+ }
39
54
  function usesHttps(url) {
40
55
  try {
41
56
  return new URL(url).protocol === "https:";
@@ -56,8 +71,26 @@ function extraCaCertificates() {
56
71
  return [];
57
72
  }
58
73
  }
59
- function caBundle() {
60
- return [...rootCertificates, ...extraCaCertificates()];
74
+ function nodeCaCertificates(source) {
75
+ const getter = tls.getCACertificates;
76
+ if (typeof getter !== "function") {
77
+ return [];
78
+ }
79
+ try {
80
+ return getter(source);
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ export function gatewayTrustedCaCertificates() {
87
+ return Array.from(new Set([
88
+ ...nodeCaCertificates("default"),
89
+ ...tls.rootCertificates,
90
+ ...nodeCaCertificates("system"),
91
+ ...nodeCaCertificates("extra"),
92
+ ...extraCaCertificates(),
93
+ ]));
61
94
  }
62
95
  function bodyToString(body) {
63
96
  if (body === undefined || body === null) {
@@ -78,7 +111,7 @@ function fetchWithCaBundle(url, init) {
78
111
  const req = httpsRequest(url, {
79
112
  method: init.method ?? "GET",
80
113
  headers,
81
- ca: [...caBundle()],
114
+ ca: [...gatewayTrustedCaCertificates()],
82
115
  signal: init.signal ?? undefined,
83
116
  }, (res) => {
84
117
  const chunks = [];
@@ -112,7 +145,7 @@ export async function gatewayFetch(url, options = {}) {
112
145
  return await doFetch(url, init);
113
146
  }
114
147
  catch (error) {
115
- if (useCaFallback && usesHttps(url) && isMissingIssuerError(error)) {
148
+ if (useCaFallback && usesHttps(url) && isRecoverableTlsTrustError(error)) {
116
149
  return fetchWithCaBundle(url, init);
117
150
  }
118
151
  throw error;
@@ -1,7 +1,7 @@
1
1
  import type { Clock } from "../gateway/types.js";
2
2
  import type { EventSink, Fingerprinter, IdSource, ModelPort, ToolPort } from "./ports.js";
3
3
  import { type HarnessLimits, type RunResult, type TaskInput } from "./types.js";
4
- export declare const HARNESS_VERSION = "0.1.4";
4
+ export declare const HARNESS_VERSION = "0.1.6";
5
5
  export interface AgentConfig {
6
6
  readonly model: string;
7
7
  readonly workingDirectory: string;
@@ -10,7 +10,7 @@ import { runLoop } from "./loop.js";
10
10
  import { MemoryEventSink } from "./sinks.js";
11
11
  import { resolveTaskPlan } from "./tasks/policy.js";
12
12
  import { DEFAULT_LIMITS, } from "./types.js";
13
- export const HARNESS_VERSION = "0.1.4";
13
+ export const HARNESS_VERSION = "0.1.6";
14
14
  function resolveLimits(config) {
15
15
  return { ...DEFAULT_LIMITS, ...config.limits };
16
16
  }
@@ -1,4 +1,4 @@
1
- export declare const SDK_VERSION = "0.1.4";
1
+ export declare const SDK_VERSION = "0.1.6";
2
2
  export { createSession, type AgentConfig, type AgentSession, type HarnessDeps, type RunResult, type TaskInput, type TaskType, } from "../harness/index.js";
3
3
  export { runAgent, type SdkAgentConfig, type SdkEvidenceOptions } from "./run-agent.js";
4
4
  export { buildWorkspaceSummary, detectWorkspace, summarizeForAudit, type AuditEntry, type AuditSummary, type ContextEntrySummary, type ContextPackSummary, type WorkspaceInfo, type WorkspaceSummary, } from "../workspace/index.js";
package/dist/sdk/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Single-sourced package version; CLI and SDK both read this to avoid drift.
2
- export const SDK_VERSION = "0.1.4";
2
+ export const SDK_VERSION = "0.1.6";
3
3
  // The typed agent surface. AgentConfig, the session factory, the run result, and the
4
4
  // session handle all live in the harness module (ADR-0004); the SDK re-exports them so
5
5
  // callers import the agent API from one place.
@@ -1,15 +1,15 @@
1
1
  [
2
+ "'sha256-2RZA1N+nLgvl8WcE8JsSdNjQ5MQUhxR2msRW7usAVlM='",
2
3
  "'sha256-6JBR+C4qigE40tMbninopqoSguR9H/AhsfWgllRr6y0='",
4
+ "'sha256-C8fp9ZeiwdGAbedIsOvEa2BMkNdJdGys5/hhETqHGL4='",
3
5
  "'sha256-FhLHRUQz4c4ntLU9VkfEesX7PnzNLENSe/16Hi523Kk='",
4
- "'sha256-M+Hl3si599YRqfbrXwUJ7SE0nbdc4ewUl5kYEqTyPkY='",
5
6
  "'sha256-NMmsYxPlvKu6BMNDUuiUA/0HWXXhODWSkUJ3CrerHAI='",
6
7
  "'sha256-OBTN3RiyCV4Bq7dFqZ5a2pAXjnCcCYeTJMO2I/LYKeo='",
7
- "'sha256-QNiMmKndkFV2DCDiccdY8YAY8jMbQzYQN++NcsQqdeI='",
8
- "'sha256-RU8ypFPeRhJ9iyjWdA0gQuMIMNNMmDfpfTvZ8mGo34w='",
9
- "'sha256-RgGBVXhSba32fqKyKK1LJ4hBlUyuzsh4X4Wz8X/2ViQ='",
10
8
  "'sha256-U9W+ZoRW19rf6ohEfUh2oSN8UmJ8mZjCoxp31AbEGYM='",
11
- "'sha256-Zh9WKxSpBbdbuXpYaiD8KL+3eIzvCyeUVc9aNk+MlSs='",
9
+ "'sha256-WS6pJFhpUaJbGkmmJZnnoqzb3ZD6Hp3TSAsAkiW/1JA='",
10
+ "'sha256-aWdClXArxjcSI0zen9sFWmTTb6X31A25YJP9LyQG2ZI='",
12
11
  "'sha256-bg+CWjI8RppcgHYH6RuW4z4OnLAUEUPDXRoYUo9Tyok='",
12
+ "'sha256-gZGxfXq2015S5Q+DB4YqyrPelc2Pxtxzxpv6HBl/Ogg='",
13
13
  "'sha256-qBQ7RdQKJEJuW7Fj1MbGjDbF6lnRdfu+KV0V4A5MTRg='",
14
14
  "'sha256-qjuzziE6xLU3Cras89VlShlRYHgYZuOxceXUDmuvClo='",
15
15
  "'sha256-xLP5QIbvR88RAxDKoSWqs6CVxNIRu17hhr7S/Q6hlU0='",
@@ -1 +1 @@
1
- <!DOCTYPE html><!--_up3xgs7rHI2vcDDL0kQ3--><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/3f36649d8ca8f3e7.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-4a462cecab786e93.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-d47fd57964443afe.js" async=""></script><script src="/_next/static/chunks/main-app-e8144a306630b76d.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Keiko</title><meta name="description" content="Keiko local developer-assist workspace."/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-4a462cecab786e93.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[4431,[],\"OutletBoundary\"]\n6:I[5278,[],\"AsyncMetadataOutlet\"]\n8:I[4431,[],\"ViewportBoundary\"]\na:I[4431,[],\"MetadataBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[7150,[],\"\"]\n:HL[\"/_next/static/css/3f36649d8ca8f3e7.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"_up3xgs7rHI2vcDDL0kQ3\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3f36649d8ca8f3e7.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L4\",null,{\"children\":[\"$L5\",[\"$\",\"$L6\",null,{\"promise\":\"$@7\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],null],[\"$\",\"$La\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$b\",null,{\"fallback\":null,\"children\":\"$Lc\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,"7:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Keiko\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Keiko local developer-assist workspace.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"c:\"$7:metadata\"\n"])</script></body></html>
1
+ <!DOCTYPE html><!--vHP365Z8U38dG6vdAzGnl--><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/3f36649d8ca8f3e7.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-4a462cecab786e93.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-d47fd57964443afe.js" async=""></script><script src="/_next/static/chunks/main-app-e8144a306630b76d.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Keiko</title><meta name="description" content="Keiko local developer-assist workspace."/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-4a462cecab786e93.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[4431,[],\"OutletBoundary\"]\n6:I[5278,[],\"AsyncMetadataOutlet\"]\n8:I[4431,[],\"ViewportBoundary\"]\na:I[4431,[],\"MetadataBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[7150,[],\"\"]\n:HL[\"/_next/static/css/3f36649d8ca8f3e7.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"vHP365Z8U38dG6vdAzGnl\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3f36649d8ca8f3e7.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L4\",null,{\"children\":[\"$L5\",[\"$\",\"$L6\",null,{\"promise\":\"$@7\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],null],[\"$\",\"$La\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$b\",null,{\"fallback\":null,\"children\":\"$Lc\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,"7:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Keiko\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Keiko local developer-assist workspace.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"c:\"$7:metadata\"\n"])</script></body></html>