@jonsoc/app 1.1.34

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.
Files changed (139) hide show
  1. package/AGENTS.md +30 -0
  2. package/README.md +51 -0
  3. package/bunfig.toml +2 -0
  4. package/e2e/context.spec.ts +45 -0
  5. package/e2e/file-open.spec.ts +23 -0
  6. package/e2e/file-viewer.spec.ts +35 -0
  7. package/e2e/fixtures.ts +40 -0
  8. package/e2e/home.spec.ts +21 -0
  9. package/e2e/model-picker.spec.ts +43 -0
  10. package/e2e/navigation.spec.ts +9 -0
  11. package/e2e/palette.spec.ts +15 -0
  12. package/e2e/prompt-mention.spec.ts +26 -0
  13. package/e2e/prompt-slash-open.spec.ts +22 -0
  14. package/e2e/prompt.spec.ts +62 -0
  15. package/e2e/session.spec.ts +21 -0
  16. package/e2e/settings.spec.ts +44 -0
  17. package/e2e/sidebar.spec.ts +21 -0
  18. package/e2e/terminal-init.spec.ts +25 -0
  19. package/e2e/terminal.spec.ts +16 -0
  20. package/e2e/tsconfig.json +8 -0
  21. package/e2e/utils.ts +38 -0
  22. package/happydom.ts +75 -0
  23. package/index.html +23 -0
  24. package/package.json +72 -0
  25. package/playwright.config.ts +43 -0
  26. package/public/_headers +17 -0
  27. package/public/apple-touch-icon-v3.png +1 -0
  28. package/public/apple-touch-icon.png +1 -0
  29. package/public/favicon-96x96-v3.png +1 -0
  30. package/public/favicon-96x96.png +1 -0
  31. package/public/favicon-v3.ico +1 -0
  32. package/public/favicon-v3.svg +1 -0
  33. package/public/favicon.ico +1 -0
  34. package/public/favicon.svg +1 -0
  35. package/public/oc-theme-preload.js +28 -0
  36. package/public/site.webmanifest +1 -0
  37. package/public/social-share-zen.png +1 -0
  38. package/public/social-share.png +1 -0
  39. package/public/web-app-manifest-192x192.png +1 -0
  40. package/public/web-app-manifest-512x512.png +1 -0
  41. package/script/e2e-local.ts +143 -0
  42. package/src/addons/serialize.test.ts +319 -0
  43. package/src/addons/serialize.ts +591 -0
  44. package/src/app.tsx +150 -0
  45. package/src/components/dialog-connect-provider.tsx +428 -0
  46. package/src/components/dialog-edit-project.tsx +259 -0
  47. package/src/components/dialog-fork.tsx +104 -0
  48. package/src/components/dialog-manage-models.tsx +59 -0
  49. package/src/components/dialog-select-directory.tsx +208 -0
  50. package/src/components/dialog-select-file.tsx +196 -0
  51. package/src/components/dialog-select-mcp.tsx +96 -0
  52. package/src/components/dialog-select-model-unpaid.tsx +130 -0
  53. package/src/components/dialog-select-model.tsx +162 -0
  54. package/src/components/dialog-select-provider.tsx +70 -0
  55. package/src/components/dialog-select-server.tsx +249 -0
  56. package/src/components/dialog-settings.tsx +112 -0
  57. package/src/components/file-tree.tsx +112 -0
  58. package/src/components/link.tsx +17 -0
  59. package/src/components/model-tooltip.tsx +91 -0
  60. package/src/components/prompt-input.tsx +2076 -0
  61. package/src/components/session/index.ts +5 -0
  62. package/src/components/session/session-context-tab.tsx +428 -0
  63. package/src/components/session/session-header.tsx +343 -0
  64. package/src/components/session/session-new-view.tsx +93 -0
  65. package/src/components/session/session-sortable-tab.tsx +56 -0
  66. package/src/components/session/session-sortable-terminal-tab.tsx +187 -0
  67. package/src/components/session-context-usage.tsx +113 -0
  68. package/src/components/session-lsp-indicator.tsx +42 -0
  69. package/src/components/session-mcp-indicator.tsx +34 -0
  70. package/src/components/settings-agents.tsx +15 -0
  71. package/src/components/settings-commands.tsx +15 -0
  72. package/src/components/settings-general.tsx +306 -0
  73. package/src/components/settings-keybinds.tsx +437 -0
  74. package/src/components/settings-mcp.tsx +15 -0
  75. package/src/components/settings-models.tsx +15 -0
  76. package/src/components/settings-permissions.tsx +234 -0
  77. package/src/components/settings-providers.tsx +15 -0
  78. package/src/components/terminal.tsx +315 -0
  79. package/src/components/titlebar.tsx +156 -0
  80. package/src/context/command.tsx +308 -0
  81. package/src/context/comments.tsx +140 -0
  82. package/src/context/file.tsx +409 -0
  83. package/src/context/global-sdk.tsx +106 -0
  84. package/src/context/global-sync.tsx +898 -0
  85. package/src/context/language.tsx +161 -0
  86. package/src/context/layout-scroll.test.ts +73 -0
  87. package/src/context/layout-scroll.ts +118 -0
  88. package/src/context/layout.tsx +648 -0
  89. package/src/context/local.tsx +578 -0
  90. package/src/context/notification.tsx +173 -0
  91. package/src/context/permission.tsx +167 -0
  92. package/src/context/platform.tsx +59 -0
  93. package/src/context/prompt.tsx +245 -0
  94. package/src/context/sdk.tsx +48 -0
  95. package/src/context/server.tsx +214 -0
  96. package/src/context/settings.tsx +166 -0
  97. package/src/context/sync.tsx +320 -0
  98. package/src/context/terminal.tsx +267 -0
  99. package/src/custom-elements.d.ts +17 -0
  100. package/src/entry.tsx +76 -0
  101. package/src/env.d.ts +8 -0
  102. package/src/hooks/use-providers.ts +31 -0
  103. package/src/i18n/ar.ts +656 -0
  104. package/src/i18n/br.ts +667 -0
  105. package/src/i18n/da.ts +582 -0
  106. package/src/i18n/de.ts +591 -0
  107. package/src/i18n/en.ts +665 -0
  108. package/src/i18n/es.ts +585 -0
  109. package/src/i18n/fr.ts +592 -0
  110. package/src/i18n/ja.ts +579 -0
  111. package/src/i18n/ko.ts +580 -0
  112. package/src/i18n/no.ts +602 -0
  113. package/src/i18n/pl.ts +661 -0
  114. package/src/i18n/ru.ts +664 -0
  115. package/src/i18n/zh.ts +574 -0
  116. package/src/i18n/zht.ts +570 -0
  117. package/src/index.css +57 -0
  118. package/src/index.ts +2 -0
  119. package/src/pages/directory-layout.tsx +57 -0
  120. package/src/pages/error.tsx +290 -0
  121. package/src/pages/home.tsx +125 -0
  122. package/src/pages/layout.tsx +2599 -0
  123. package/src/pages/session.tsx +2505 -0
  124. package/src/sst-env.d.ts +10 -0
  125. package/src/utils/dom.ts +51 -0
  126. package/src/utils/id.ts +99 -0
  127. package/src/utils/index.ts +1 -0
  128. package/src/utils/perf.ts +135 -0
  129. package/src/utils/persist.ts +377 -0
  130. package/src/utils/prompt.ts +203 -0
  131. package/src/utils/same.ts +6 -0
  132. package/src/utils/solid-dnd.tsx +55 -0
  133. package/src/utils/sound.ts +110 -0
  134. package/src/utils/speech.ts +302 -0
  135. package/src/utils/worktree.ts +58 -0
  136. package/sst-env.d.ts +9 -0
  137. package/tsconfig.json +26 -0
  138. package/vite.config.ts +15 -0
  139. package/vite.js +26 -0
package/happydom.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { GlobalRegistrator } from "@happy-dom/global-registrator"
2
+
3
+ GlobalRegistrator.register()
4
+
5
+ const originalGetContext = HTMLCanvasElement.prototype.getContext
6
+ // @ts-expect-error - we're overriding with a simplified mock
7
+ HTMLCanvasElement.prototype.getContext = function (contextType: string, _options?: unknown) {
8
+ if (contextType === "2d") {
9
+ return {
10
+ canvas: this,
11
+ fillStyle: "#000000",
12
+ strokeStyle: "#000000",
13
+ font: "12px monospace",
14
+ textAlign: "start",
15
+ textBaseline: "alphabetic",
16
+ globalAlpha: 1,
17
+ globalCompositeOperation: "source-over",
18
+ imageSmoothingEnabled: true,
19
+ lineWidth: 1,
20
+ lineCap: "butt",
21
+ lineJoin: "miter",
22
+ miterLimit: 10,
23
+ shadowBlur: 0,
24
+ shadowColor: "rgba(0, 0, 0, 0)",
25
+ shadowOffsetX: 0,
26
+ shadowOffsetY: 0,
27
+ fillRect: () => {},
28
+ strokeRect: () => {},
29
+ clearRect: () => {},
30
+ fillText: () => {},
31
+ strokeText: () => {},
32
+ measureText: (text: string) => ({ width: text.length * 8 }),
33
+ drawImage: () => {},
34
+ save: () => {},
35
+ restore: () => {},
36
+ scale: () => {},
37
+ rotate: () => {},
38
+ translate: () => {},
39
+ transform: () => {},
40
+ setTransform: () => {},
41
+ resetTransform: () => {},
42
+ createLinearGradient: () => ({ addColorStop: () => {} }),
43
+ createRadialGradient: () => ({ addColorStop: () => {} }),
44
+ createPattern: () => null,
45
+ beginPath: () => {},
46
+ closePath: () => {},
47
+ moveTo: () => {},
48
+ lineTo: () => {},
49
+ bezierCurveTo: () => {},
50
+ quadraticCurveTo: () => {},
51
+ arc: () => {},
52
+ arcTo: () => {},
53
+ ellipse: () => {},
54
+ rect: () => {},
55
+ fill: () => {},
56
+ stroke: () => {},
57
+ clip: () => {},
58
+ isPointInPath: () => false,
59
+ isPointInStroke: () => false,
60
+ getTransform: () => ({}),
61
+ getImageData: () => ({
62
+ data: new Uint8ClampedArray(0),
63
+ width: 0,
64
+ height: 0,
65
+ }),
66
+ putImageData: () => {},
67
+ createImageData: () => ({
68
+ data: new Uint8ClampedArray(0),
69
+ width: 0,
70
+ height: 0,
71
+ }),
72
+ } as unknown as CanvasRenderingContext2D
73
+ }
74
+ return originalGetContext.call(this, contextType as "2d", _options)
75
+ }
package/index.html ADDED
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html lang="en" style="background-color: var(--background-base)">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>OpenCode</title>
7
+ <link rel="icon" type="image/png" href="/favicon-96x96-v3.png" sizes="96x96" />
8
+ <link rel="icon" type="image/svg+xml" href="/favicon-v3.svg" />
9
+ <link rel="shortcut icon" href="/favicon-v3.ico" />
10
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-v3.png" />
11
+ <link rel="manifest" href="/site.webmanifest" />
12
+ <meta name="theme-color" content="#F8F7F7" />
13
+ <meta name="theme-color" content="#131010" media="(prefers-color-scheme: dark)" />
14
+ <meta property="og:image" content="/social-share.png" />
15
+ <meta property="twitter:image" content="/social-share.png" />
16
+ <script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>
17
+ </head>
18
+ <body class="antialiased overscroll-none text-12-regular overflow-hidden">
19
+ <noscript>You need to enable JavaScript to run this app.</noscript>
20
+ <div id="root" class="flex flex-col h-dvh p-px"></div>
21
+ <script src="/src/entry.tsx" type="module"></script>
22
+ </body>
23
+ </html>
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@jonsoc/app",
3
+ "version": "1.1.34",
4
+ "description": "",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts",
8
+ "./vite": "./vite.js"
9
+ },
10
+ "scripts": {
11
+ "typecheck": "tsgo -b",
12
+ "start": "vite",
13
+ "dev": "vite",
14
+ "build": "vite build",
15
+ "serve": "vite preview",
16
+ "test": "playwright test",
17
+ "test:e2e": "playwright test",
18
+ "test:e2e:local": "bun script/e2e-local.ts",
19
+ "test:e2e:ui": "playwright test --ui",
20
+ "test:e2e:report": "playwright show-report e2e/playwright-report"
21
+ },
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "@happy-dom/global-registrator": "20.0.11",
25
+ "@playwright/test": "1.57.0",
26
+ "@tailwindcss/vite": "catalog:",
27
+ "@tsconfig/bun": "1.0.9",
28
+ "@types/bun": "catalog:",
29
+ "@types/luxon": "catalog:",
30
+ "@types/node": "catalog:",
31
+ "@typescript/native-preview": "catalog:",
32
+ "typescript": "catalog:",
33
+ "vite": "catalog:",
34
+ "vite-plugin-icons-spritesheet": "3.0.1",
35
+ "vite-plugin-solid": "catalog:"
36
+ },
37
+ "dependencies": {
38
+ "@kobalte/core": "catalog:",
39
+ "@jonsoc/sdk": "workspace:*",
40
+ "@jonsoc/ui": "workspace:*",
41
+ "@jonsoc/util": "workspace:*",
42
+ "@pierre/diffs": "catalog:",
43
+ "@shikijs/transformers": "3.9.2",
44
+ "@solid-primitives/active-element": "2.1.3",
45
+ "@solid-primitives/audio": "1.4.2",
46
+ "@solid-primitives/i18n": "2.2.1",
47
+ "@solid-primitives/event-bus": "1.1.2",
48
+ "@solid-primitives/media": "2.3.3",
49
+ "@solid-primitives/resize-observer": "2.1.3",
50
+ "@solid-primitives/scroll": "2.1.3",
51
+ "@solid-primitives/storage": "catalog:",
52
+ "@solid-primitives/websocket": "1.3.1",
53
+ "@solidjs/meta": "catalog:",
54
+ "@solidjs/router": "catalog:",
55
+ "@thisbeyond/solid-dnd": "0.7.5",
56
+ "diff": "catalog:",
57
+ "fuzzysort": "catalog:",
58
+ "ghostty-web": "0.3.0",
59
+ "luxon": "catalog:",
60
+ "marked": "catalog:",
61
+ "marked-shiki": "catalog:",
62
+ "remeda": "catalog:",
63
+ "shiki": "catalog:",
64
+ "solid-js": "catalog:",
65
+ "tailwindcss": "catalog:",
66
+ "virtua": "catalog:",
67
+ "zod": "catalog:"
68
+ },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ }
72
+ }
@@ -0,0 +1,43 @@
1
+ import { defineConfig, devices } from "@playwright/test"
2
+
3
+ const port = Number(process.env.PLAYWRIGHT_PORT ?? 3000)
4
+ const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? `http://localhost:${port}`
5
+ const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "localhost"
6
+ const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
7
+ const command = `bun run dev -- --host 0.0.0.0 --port ${port}`
8
+ const reuse = !process.env.CI
9
+
10
+ export default defineConfig({
11
+ testDir: "./e2e",
12
+ outputDir: "./e2e/test-results",
13
+ timeout: 60_000,
14
+ expect: {
15
+ timeout: 10_000,
16
+ },
17
+ fullyParallel: true,
18
+ forbidOnly: !!process.env.CI,
19
+ retries: process.env.CI ? 2 : 0,
20
+ reporter: [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]],
21
+ webServer: {
22
+ command,
23
+ url: baseURL,
24
+ reuseExistingServer: reuse,
25
+ timeout: 120_000,
26
+ env: {
27
+ VITE_OPENCODE_SERVER_HOST: serverHost,
28
+ VITE_OPENCODE_SERVER_PORT: serverPort,
29
+ },
30
+ },
31
+ use: {
32
+ baseURL,
33
+ trace: "on-first-retry",
34
+ screenshot: "only-on-failure",
35
+ video: "retain-on-failure",
36
+ },
37
+ projects: [
38
+ {
39
+ name: "chromium",
40
+ use: { ...devices["Desktop Chrome"] },
41
+ },
42
+ ],
43
+ })
@@ -0,0 +1,17 @@
1
+ /assets/*.js
2
+ Content-Type: application/javascript
3
+
4
+ /assets/*.mjs
5
+ Content-Type: application/javascript
6
+
7
+ /assets/*.css
8
+ Content-Type: text/css
9
+
10
+ /*.js
11
+ Content-Type: application/javascript
12
+
13
+ /*.mjs
14
+ Content-Type: application/javascript
15
+
16
+ /*.css
17
+ Content-Type: text/css
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/apple-touch-icon-v3.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/apple-touch-icon.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon-96x96-v3.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon-96x96.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon-v3.ico
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon-v3.svg
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon.ico
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/favicon.svg
@@ -0,0 +1,28 @@
1
+ ;(function () {
2
+ var themeId = localStorage.getItem("opencode-theme-id")
3
+ if (!themeId) return
4
+
5
+ var scheme = localStorage.getItem("opencode-color-scheme") || "system"
6
+ var isDark = scheme === "dark" || (scheme === "system" && matchMedia("(prefers-color-scheme: dark)").matches)
7
+ var mode = isDark ? "dark" : "light"
8
+
9
+ document.documentElement.dataset.theme = themeId
10
+ document.documentElement.dataset.colorScheme = mode
11
+
12
+ if (themeId === "oc-1") return
13
+
14
+ var css = localStorage.getItem("opencode-theme-css-" + themeId + "-" + mode)
15
+ if (css) {
16
+ var style = document.createElement("style")
17
+ style.id = "oc-theme-preload"
18
+ style.textContent =
19
+ ":root{color-scheme:" +
20
+ mode +
21
+ ";--text-mix-blend-mode:" +
22
+ (isDark ? "plus-lighter" : "multiply") +
23
+ ";" +
24
+ css +
25
+ "}"
26
+ document.head.appendChild(style)
27
+ }
28
+ })()
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/site.webmanifest
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/images/social-share-zen.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/images/social-share.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/web-app-manifest-192x192.png
@@ -0,0 +1 @@
1
+ ../../ui/src/assets/favicon/web-app-manifest-512x512.png
@@ -0,0 +1,143 @@
1
+ import fs from "node:fs/promises"
2
+ import net from "node:net"
3
+ import os from "node:os"
4
+ import path from "node:path"
5
+
6
+ async function freePort() {
7
+ return await new Promise<number>((resolve, reject) => {
8
+ const server = net.createServer()
9
+ server.once("error", reject)
10
+ server.listen(0, () => {
11
+ const address = server.address()
12
+ if (!address || typeof address === "string") {
13
+ server.close(() => reject(new Error("Failed to acquire a free port")))
14
+ return
15
+ }
16
+ server.close((err) => {
17
+ if (err) {
18
+ reject(err)
19
+ return
20
+ }
21
+ resolve(address.port)
22
+ })
23
+ })
24
+ })
25
+ }
26
+
27
+ async function waitForHealth(url: string) {
28
+ const timeout = Date.now() + 120_000
29
+ const errors: string[] = []
30
+ while (Date.now() < timeout) {
31
+ const result = await fetch(url)
32
+ .then((r) => ({ ok: r.ok, error: undefined }))
33
+ .catch((error) => ({
34
+ ok: false,
35
+ error: error instanceof Error ? error.message : String(error),
36
+ }))
37
+ if (result.ok) return
38
+ if (result.error) errors.push(result.error)
39
+ await new Promise((r) => setTimeout(r, 250))
40
+ }
41
+ const last = errors.length ? ` (last error: ${errors[errors.length - 1]})` : ""
42
+ throw new Error(`Timed out waiting for server health: ${url}${last}`)
43
+ }
44
+
45
+ const appDir = process.cwd()
46
+ const repoDir = path.resolve(appDir, "../..")
47
+ const jonsocDir = path.join(repoDir, "packages", "jonsoc")
48
+ const modelsJson = path.join(jonsocDir, "test", "tool", "fixtures", "models-api.json")
49
+
50
+ const extraArgs = (() => {
51
+ const args = process.argv.slice(2)
52
+ if (args[0] === "--") return args.slice(1)
53
+ return args
54
+ })()
55
+
56
+ const [serverPort, webPort] = await Promise.all([freePort(), freePort()])
57
+
58
+ const sandbox = await fs.mkdtemp(path.join(os.tmpdir(), "jonsoc-e2e-"))
59
+
60
+ const serverEnv = {
61
+ ...process.env,
62
+ MODELS_DEV_API_JSON: modelsJson,
63
+ OPENCODE_DISABLE_MODELS_FETCH: "true",
64
+ OPENCODE_DISABLE_SHARE: "true",
65
+ OPENCODE_DISABLE_LSP_DOWNLOAD: "true",
66
+ OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
67
+ OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: "true",
68
+ OPENCODE_TEST_HOME: path.join(sandbox, "home"),
69
+ XDG_DATA_HOME: path.join(sandbox, "share"),
70
+ XDG_CACHE_HOME: path.join(sandbox, "cache"),
71
+ XDG_CONFIG_HOME: path.join(sandbox, "config"),
72
+ XDG_STATE_HOME: path.join(sandbox, "state"),
73
+ OPENCODE_E2E_PROJECT_DIR: repoDir,
74
+ OPENCODE_E2E_SESSION_TITLE: "E2E Session",
75
+ OPENCODE_E2E_MESSAGE: "Seeded for UI e2e",
76
+ OPENCODE_E2E_MODEL: "jonsoc/gpt-5-nano",
77
+ OPENCODE_CLIENT: "app",
78
+ } satisfies Record<string, string>
79
+
80
+ const runnerEnv = {
81
+ ...serverEnv,
82
+ PLAYWRIGHT_SERVER_HOST: "127.0.0.1",
83
+ PLAYWRIGHT_SERVER_PORT: String(serverPort),
84
+ VITE_OPENCODE_SERVER_HOST: "127.0.0.1",
85
+ VITE_OPENCODE_SERVER_PORT: String(serverPort),
86
+ PLAYWRIGHT_PORT: String(webPort),
87
+ } satisfies Record<string, string>
88
+
89
+ const seed = Bun.spawn(["bun", "script/seed-e2e.ts"], {
90
+ cwd: jonsocDir,
91
+ env: serverEnv,
92
+ stdout: "inherit",
93
+ stderr: "inherit",
94
+ })
95
+
96
+ const seedExit = await seed.exited
97
+ if (seedExit !== 0) {
98
+ process.exit(seedExit)
99
+ }
100
+
101
+ Object.assign(process.env, serverEnv)
102
+ process.env.AGENT = "1"
103
+ process.env.OPENCODE = "1"
104
+
105
+ const log = await import("../../jonsoc/src/util/log")
106
+ const install = await import("../../jonsoc/src/installation")
107
+ await log.Log.init({
108
+ print: true,
109
+ dev: install.Installation.isLocal(),
110
+ level: "WARN",
111
+ })
112
+
113
+ const servermod = await import("../../jonsoc/src/server/server")
114
+ const inst = await import("../../jonsoc/src/project/instance")
115
+ const server = servermod.Server.listen({ port: serverPort, hostname: "127.0.0.1" })
116
+ console.log(`jonsoc server listening on http://127.0.0.1:${serverPort}`)
117
+
118
+ const result = await (async () => {
119
+ try {
120
+ await waitForHealth(`http://127.0.0.1:${serverPort}/global/health`)
121
+
122
+ const runner = Bun.spawn(["bun", "test:e2e", ...extraArgs], {
123
+ cwd: appDir,
124
+ env: runnerEnv,
125
+ stdout: "inherit",
126
+ stderr: "inherit",
127
+ })
128
+
129
+ return { code: await runner.exited }
130
+ } catch (error) {
131
+ return { error }
132
+ } finally {
133
+ await inst.Instance.disposeAll()
134
+ await server.stop()
135
+ }
136
+ })()
137
+
138
+ if ("error" in result) {
139
+ console.error(result.error)
140
+ process.exit(1)
141
+ }
142
+
143
+ process.exit(result.code)