@agentprojectcontext/apx 1.15.6 → 1.17.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.
Files changed (222) hide show
  1. package/package.json +46 -5
  2. package/src/cli/commands/log.js +113 -0
  3. package/src/cli/commands/overlay.js +253 -0
  4. package/src/cli/commands/sys.js +88 -16
  5. package/src/cli/index.js +23 -1
  6. package/src/cli/terminal-chat/renderer.js +71 -56
  7. package/src/cli-ts/commands/agent.ts +173 -0
  8. package/src/cli-ts/commands/chat.ts +119 -0
  9. package/src/cli-ts/commands/daemon.ts +112 -0
  10. package/src/cli-ts/commands/exec.ts +109 -0
  11. package/src/cli-ts/commands/mcp.ts +235 -0
  12. package/src/cli-ts/commands/session.ts +224 -0
  13. package/src/cli-ts/commands/status.ts +61 -0
  14. package/src/cli-ts/http.ts +36 -0
  15. package/src/cli-ts/index.ts +73 -0
  16. package/src/cli-ts/ui.ts +107 -0
  17. package/src/core/logging.js +81 -0
  18. package/src/daemon/api.js +58 -0
  19. package/src/daemon/engines/anthropic.js +60 -1
  20. package/src/daemon/engines/index.js +2 -1
  21. package/src/daemon/engines/ollama.js +70 -3
  22. package/src/daemon/index.js +58 -0
  23. package/src/daemon/overlay-ws.js +40 -0
  24. package/src/daemon/plugins/index.js +2 -1
  25. package/src/daemon/plugins/overlay.js +177 -0
  26. package/src/daemon/plugins/telegram.js +15 -3
  27. package/src/daemon/super-agent-langchain.js +296 -0
  28. package/src/daemon/super-agent.js +115 -19
  29. package/src/daemon/transcription.js +262 -59
  30. package/src/daemon/whisper-server.py +57 -6
  31. package/src/overlay/index.html +44 -0
  32. package/src/overlay/main.js +480 -0
  33. package/src/overlay/package.json +3 -0
  34. package/src/overlay/preload.js +34 -0
  35. package/src/overlay/renderer.js +371 -0
  36. package/src/overlay/style.css +250 -0
  37. package/src/tui/_shims/cli-error.ts +6 -0
  38. package/src/tui/_shims/cli-logo.ts +18 -0
  39. package/src/tui/_shims/cli-ui.ts +1 -0
  40. package/src/tui/_shims/config-console-state.ts +7 -0
  41. package/src/tui/_shims/core-any.ts +30 -0
  42. package/src/tui/_shims/core-binary.ts +13 -0
  43. package/src/tui/_shims/core-flag.ts +3 -0
  44. package/src/tui/_shims/core-log.ts +14 -0
  45. package/src/tui/_shims/lsp-language.ts +1 -0
  46. package/src/tui/_shims/opencode-any.ts +135 -0
  47. package/src/tui/_shims/opencode-sdk-v2.ts +48 -0
  48. package/src/tui/_shims/plugin-tui.ts +13 -0
  49. package/src/tui/_shims/provider-provider.ts +10 -0
  50. package/src/tui/_shims/session-retry.ts +1 -0
  51. package/src/tui/_shims/session-schema.ts +15 -0
  52. package/src/tui/_shims/session-session.ts +3 -0
  53. package/src/tui/_shims/snapshot.ts +4 -0
  54. package/src/tui/_shims/tool-any.ts +18 -0
  55. package/src/tui/_shims/util-error.ts +7 -0
  56. package/src/tui/_shims/util-filesystem.ts +79 -0
  57. package/src/tui/_shims/util-format.ts +7 -0
  58. package/src/tui/_shims/util-iife.ts +3 -0
  59. package/src/tui/_shims/util-locale.ts +10 -0
  60. package/src/tui/_shims/util-process.ts +38 -0
  61. package/src/tui/app.tsx +783 -0
  62. package/src/tui/asset/charge.wav +0 -0
  63. package/src/tui/asset/pulse-a.wav +0 -0
  64. package/src/tui/asset/pulse-b.wav +0 -0
  65. package/src/tui/asset/pulse-c.wav +0 -0
  66. package/src/tui/attach.ts +100 -0
  67. package/src/tui/component/bg-pulse-render.ts +436 -0
  68. package/src/tui/component/bg-pulse.tsx +99 -0
  69. package/src/tui/component/border.tsx +21 -0
  70. package/src/tui/component/dialog-agent.tsx +31 -0
  71. package/src/tui/component/dialog-console-org.tsx +103 -0
  72. package/src/tui/component/dialog-mcp.tsx +85 -0
  73. package/src/tui/component/dialog-model.tsx +175 -0
  74. package/src/tui/component/dialog-provider.tsx +456 -0
  75. package/src/tui/component/dialog-retry-action.tsx +160 -0
  76. package/src/tui/component/dialog-session-delete-failed.tsx +99 -0
  77. package/src/tui/component/dialog-session-list.tsx +323 -0
  78. package/src/tui/component/dialog-session-rename.tsx +31 -0
  79. package/src/tui/component/dialog-skill.tsx +36 -0
  80. package/src/tui/component/dialog-stash.tsx +87 -0
  81. package/src/tui/component/dialog-status.tsx +168 -0
  82. package/src/tui/component/dialog-tag.tsx +44 -0
  83. package/src/tui/component/dialog-theme-list.tsx +50 -0
  84. package/src/tui/component/dialog-variant.tsx +39 -0
  85. package/src/tui/component/dialog-workspace-create.tsx +302 -0
  86. package/src/tui/component/dialog-workspace-file-changes.tsx +138 -0
  87. package/src/tui/component/dialog-workspace-unavailable.tsx +69 -0
  88. package/src/tui/component/error-component.tsx +92 -0
  89. package/src/tui/component/logo.tsx +896 -0
  90. package/src/tui/component/plugin-route-missing.tsx +14 -0
  91. package/src/tui/component/prompt/autocomplete.tsx +869 -0
  92. package/src/tui/component/prompt/cwd.ts +0 -0
  93. package/src/tui/component/prompt/frecency.tsx +90 -0
  94. package/src/tui/component/prompt/history.tsx +108 -0
  95. package/src/tui/component/prompt/index.tsx +1809 -0
  96. package/src/tui/component/prompt/part.ts +16 -0
  97. package/src/tui/component/prompt/stash.tsx +101 -0
  98. package/src/tui/component/prompt/traits.ts +35 -0
  99. package/src/tui/component/spinner.tsx +24 -0
  100. package/src/tui/component/startup-loading.tsx +63 -0
  101. package/src/tui/component/todo-item.tsx +32 -0
  102. package/src/tui/component/use-connected.tsx +9 -0
  103. package/src/tui/component/workspace-label.tsx +19 -0
  104. package/src/tui/config/cwd.ts +5 -0
  105. package/src/tui/config/keybind.ts +432 -0
  106. package/src/tui/config/tui-migrate.ts +154 -0
  107. package/src/tui/config/tui-schema.ts +34 -0
  108. package/src/tui/config/tui.ts +46 -0
  109. package/src/tui/context/aggregate-failures.ts +34 -0
  110. package/src/tui/context/args.tsx +15 -0
  111. package/src/tui/context/command-palette.tsx +163 -0
  112. package/src/tui/context/directory.ts +15 -0
  113. package/src/tui/context/editor-zed.ts +283 -0
  114. package/src/tui/context/editor.ts +468 -0
  115. package/src/tui/context/event-apx.ts +22 -0
  116. package/src/tui/context/event.ts +6 -0
  117. package/src/tui/context/exit.tsx +60 -0
  118. package/src/tui/context/helper.tsx +25 -0
  119. package/src/tui/context/kv.tsx +81 -0
  120. package/src/tui/context/local.tsx +608 -0
  121. package/src/tui/context/path-format.tsx +39 -0
  122. package/src/tui/context/project-apx.tsx +48 -0
  123. package/src/tui/context/project.tsx +7 -0
  124. package/src/tui/context/prompt.tsx +18 -0
  125. package/src/tui/context/route.tsx +52 -0
  126. package/src/tui/context/sdk-apx.tsx +185 -0
  127. package/src/tui/context/sdk.tsx +6 -0
  128. package/src/tui/context/sync-apx.tsx +178 -0
  129. package/src/tui/context/sync-v2.tsx +16 -0
  130. package/src/tui/context/sync.tsx +118 -0
  131. package/src/tui/context/theme/aura.json +69 -0
  132. package/src/tui/context/theme/ayu.json +80 -0
  133. package/src/tui/context/theme/carbonfox.json +248 -0
  134. package/src/tui/context/theme/catppuccin-frappe.json +230 -0
  135. package/src/tui/context/theme/catppuccin-macchiato.json +230 -0
  136. package/src/tui/context/theme/catppuccin.json +112 -0
  137. package/src/tui/context/theme/cobalt2.json +225 -0
  138. package/src/tui/context/theme/cursor.json +249 -0
  139. package/src/tui/context/theme/dracula.json +219 -0
  140. package/src/tui/context/theme/everforest.json +241 -0
  141. package/src/tui/context/theme/flexoki.json +237 -0
  142. package/src/tui/context/theme/github.json +233 -0
  143. package/src/tui/context/theme/gruvbox.json +242 -0
  144. package/src/tui/context/theme/kanagawa.json +77 -0
  145. package/src/tui/context/theme/lucent-orng.json +234 -0
  146. package/src/tui/context/theme/material.json +235 -0
  147. package/src/tui/context/theme/matrix.json +77 -0
  148. package/src/tui/context/theme/mercury.json +252 -0
  149. package/src/tui/context/theme/monokai.json +221 -0
  150. package/src/tui/context/theme/nightowl.json +221 -0
  151. package/src/tui/context/theme/nord.json +223 -0
  152. package/src/tui/context/theme/one-dark.json +84 -0
  153. package/src/tui/context/theme/opencode.json +245 -0
  154. package/src/tui/context/theme/orng.json +249 -0
  155. package/src/tui/context/theme/osaka-jade.json +93 -0
  156. package/src/tui/context/theme/palenight.json +222 -0
  157. package/src/tui/context/theme/rosepine.json +234 -0
  158. package/src/tui/context/theme/solarized.json +223 -0
  159. package/src/tui/context/theme/synthwave84.json +226 -0
  160. package/src/tui/context/theme/tokyonight.json +243 -0
  161. package/src/tui/context/theme/vercel.json +245 -0
  162. package/src/tui/context/theme/vesper.json +218 -0
  163. package/src/tui/context/theme/zenburn.json +223 -0
  164. package/src/tui/context/theme.tsx +1247 -0
  165. package/src/tui/context/tui-config.tsx +9 -0
  166. package/src/tui/event.ts +16 -0
  167. package/src/tui/feature-plugins/home/footer.tsx +94 -0
  168. package/src/tui/feature-plugins/home/tips-view.tsx +166 -0
  169. package/src/tui/feature-plugins/home/tips.tsx +59 -0
  170. package/src/tui/feature-plugins/sidebar/context.tsx +65 -0
  171. package/src/tui/feature-plugins/sidebar/files.tsx +63 -0
  172. package/src/tui/feature-plugins/sidebar/footer.tsx +94 -0
  173. package/src/tui/feature-plugins/sidebar/lsp.tsx +65 -0
  174. package/src/tui/feature-plugins/sidebar/mcp.tsx +97 -0
  175. package/src/tui/feature-plugins/sidebar/todo.tsx +49 -0
  176. package/src/tui/feature-plugins/system/plugins.tsx +269 -0
  177. package/src/tui/feature-plugins/system/session-v2.tsx +1143 -0
  178. package/src/tui/feature-plugins/system/which-key.tsx +608 -0
  179. package/src/tui/keymap.tsx +166 -0
  180. package/src/tui/layer.ts +6 -0
  181. package/src/tui/plugin/api.tsx +381 -0
  182. package/src/tui/plugin/command-shim.ts +109 -0
  183. package/src/tui/plugin/internal.ts +33 -0
  184. package/src/tui/plugin/runtime.ts +1069 -0
  185. package/src/tui/plugin/slots.tsx +60 -0
  186. package/src/tui/routes/home.tsx +96 -0
  187. package/src/tui/routes/session/dialog-fork-from-timeline.tsx +76 -0
  188. package/src/tui/routes/session/dialog-message.tsx +108 -0
  189. package/src/tui/routes/session/dialog-subagent.tsx +26 -0
  190. package/src/tui/routes/session/dialog-timeline.tsx +47 -0
  191. package/src/tui/routes/session/footer.tsx +91 -0
  192. package/src/tui/routes/session/index.tsx +188 -0
  193. package/src/tui/routes/session/permission.tsx +722 -0
  194. package/src/tui/routes/session/question.tsx +490 -0
  195. package/src/tui/routes/session/sidebar.tsx +102 -0
  196. package/src/tui/routes/session/subagent-footer.tsx +133 -0
  197. package/src/tui/run.ts +84 -0
  198. package/src/tui/thread.ts +261 -0
  199. package/src/tui/tsconfig.json +40 -0
  200. package/src/tui/ui/dialog-alert.tsx +66 -0
  201. package/src/tui/ui/dialog-confirm.tsx +108 -0
  202. package/src/tui/ui/dialog-export-options.tsx +217 -0
  203. package/src/tui/ui/dialog-help.tsx +40 -0
  204. package/src/tui/ui/dialog-prompt.tsx +101 -0
  205. package/src/tui/ui/dialog-select.tsx +553 -0
  206. package/src/tui/ui/dialog.tsx +211 -0
  207. package/src/tui/ui/link.tsx +34 -0
  208. package/src/tui/ui/spinner.ts +368 -0
  209. package/src/tui/ui/toast.tsx +111 -0
  210. package/src/tui/util/clipboard.ts +217 -0
  211. package/src/tui/util/editor.ts +37 -0
  212. package/src/tui/util/model.ts +23 -0
  213. package/src/tui/util/provider-origin.ts +7 -0
  214. package/src/tui/util/revert-diff.ts +18 -0
  215. package/src/tui/util/scroll.ts +25 -0
  216. package/src/tui/util/selection.ts +65 -0
  217. package/src/tui/util/signal.ts +41 -0
  218. package/src/tui/util/sound.ts +156 -0
  219. package/src/tui/util/transcript.ts +112 -0
  220. package/src/tui/validate-session.ts +29 -0
  221. package/src/tui/win32.ts +130 -0
  222. package/src/tui/worker.ts +104 -0
@@ -0,0 +1,18 @@
1
+ import { createSimpleContext } from "./helper"
2
+ import type { PromptRef } from "../component/prompt"
3
+
4
+ export const { use: usePromptRef, provider: PromptRefProvider } = createSimpleContext({
5
+ name: "PromptRef",
6
+ init: () => {
7
+ let current: PromptRef | undefined
8
+
9
+ return {
10
+ get current() {
11
+ return current
12
+ },
13
+ set(ref: PromptRef | undefined) {
14
+ current = ref
15
+ },
16
+ }
17
+ },
18
+ })
@@ -0,0 +1,52 @@
1
+ import { createStore, reconcile } from "solid-js/store"
2
+ import { createSimpleContext } from "./helper"
3
+ import type { PromptInfo } from "../component/prompt/history"
4
+
5
+ export type HomeRoute = {
6
+ type: "home"
7
+ prompt?: PromptInfo
8
+ }
9
+
10
+ export type SessionRoute = {
11
+ type: "session"
12
+ sessionID: string
13
+ prompt?: PromptInfo
14
+ }
15
+
16
+ export type PluginRoute = {
17
+ type: "plugin"
18
+ id: string
19
+ data?: Record<string, unknown>
20
+ }
21
+
22
+ export type Route = HomeRoute | SessionRoute | PluginRoute
23
+
24
+ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({
25
+ name: "Route",
26
+ init: (props: { initialRoute?: Route }) => {
27
+ const [store, setStore] = createStore<Route>(
28
+ props.initialRoute ??
29
+ (process.env["OPENCODE_ROUTE"]
30
+ ? JSON.parse(process.env["OPENCODE_ROUTE"])
31
+ : {
32
+ type: "home",
33
+ }),
34
+ )
35
+
36
+ return {
37
+ get data() {
38
+ return store
39
+ },
40
+ navigate(route: Route) {
41
+ setStore(reconcile(route))
42
+ },
43
+ }
44
+ },
45
+ })
46
+
47
+ export type RouteContext = ReturnType<typeof useRoute>
48
+
49
+ export function useRouteData<T extends Route["type"]>(type: T) {
50
+ const route = useRoute()
51
+ return route.data as Extract<Route, { type: typeof type }>
52
+ }
@@ -0,0 +1,185 @@
1
+ import { createGlobalEmitter } from "@solid-primitives/event-bus"
2
+ import { createSimpleContext } from "./helper"
3
+ import { onCleanup } from "solid-js"
4
+ import fs from "node:fs"
5
+ import os from "node:os"
6
+ import path from "node:path"
7
+
8
+ const TOKEN_PATH = path.join(os.homedir(), ".apx", "daemon.token")
9
+
10
+ function readToken(): string {
11
+ try {
12
+ return fs.readFileSync(TOKEN_PATH, "utf8").trim()
13
+ } catch {
14
+ return ""
15
+ }
16
+ }
17
+
18
+ export type ApxEvent =
19
+ | { type: "session.created"; sessionID: string }
20
+ | { type: "chunk"; sessionID: string; chunk: string }
21
+ | { type: "final"; sessionID: string; text: string; usage?: { input_tokens: number; output_tokens: number } }
22
+ | { type: "error"; sessionID: string; error: string }
23
+
24
+ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
25
+ name: "SDK",
26
+ init: (props: {
27
+ url: string
28
+ pid: string
29
+ agent?: string
30
+ model?: string
31
+ // Legacy compat props (unused but accepted to avoid prop errors)
32
+ directory?: string
33
+ fetch?: typeof fetch
34
+ headers?: RequestInit["headers"]
35
+ events?: unknown
36
+ }) => {
37
+ const abort = new AbortController()
38
+ const emitter = createGlobalEmitter<{ event: ApxEvent }>()
39
+
40
+ function headers(): Record<string, string> {
41
+ const token = readToken()
42
+ return {
43
+ "content-type": "application/json",
44
+ ...(token ? { authorization: `Bearer ${token}` } : {}),
45
+ }
46
+ }
47
+
48
+ async function streamChat(
49
+ sessionID: string,
50
+ prompt: string,
51
+ previousMessages: Array<{ role: string; content: string }> = [],
52
+ ) {
53
+ const res = await fetch(`${props.url}/projects/${props.pid}/super-agent/chat/stream`, {
54
+ method: "POST",
55
+ headers: headers(),
56
+ body: JSON.stringify({ prompt, model: props.model, previousMessages }),
57
+ signal: abort.signal,
58
+ })
59
+ if (!res.ok || !res.body) throw new Error(`stream error: ${res.status}`)
60
+ const reader = res.body.getReader()
61
+ const dec = new TextDecoder()
62
+ let buf = ""
63
+ while (true) {
64
+ const { done, value } = await reader.read()
65
+ if (done) break
66
+ buf += dec.decode(value, { stream: true })
67
+ const lines = buf.split(/\r?\n/)
68
+ buf = lines.pop() ?? ""
69
+ for (const line of lines) {
70
+ if (!line.trim()) continue
71
+ try {
72
+ const ev = JSON.parse(line)
73
+ if (ev.type === "chunk") emitter.emit("event", { type: "chunk", sessionID, chunk: ev.chunk })
74
+ if (ev.type === "final")
75
+ emitter.emit("event", {
76
+ type: "final",
77
+ sessionID,
78
+ text: ev.result?.text ?? "",
79
+ usage: ev.result?.usage,
80
+ })
81
+ if (ev.type === "error") emitter.emit("event", { type: "error", sessionID, error: ev.error })
82
+ } catch {
83
+ // ignore parse errors for partial lines
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ async function createSession(): Promise<string> {
90
+ const token = readToken()
91
+ const res = await fetch(`${props.url}/projects/${props.pid}/sessions`, {
92
+ method: "POST",
93
+ headers: {
94
+ "content-type": "application/json",
95
+ ...(token ? { authorization: `Bearer ${token}` } : {}),
96
+ },
97
+ body: JSON.stringify({}),
98
+ signal: abort.signal,
99
+ })
100
+ if (!res.ok) throw new Error(`createSession: ${res.status}`)
101
+ const data = await res.json()
102
+ return (data as any).id as string
103
+ }
104
+
105
+ async function listSessions(): Promise<Array<{ id: string; title: string; updatedAt?: number }>> {
106
+ try {
107
+ const token = readToken()
108
+ const res = await fetch(`${props.url}/projects/${props.pid}/sessions`, {
109
+ headers: token ? { authorization: `Bearer ${token}` } : {},
110
+ signal: abort.signal,
111
+ })
112
+ if (!res.ok) return []
113
+ const data = await res.json()
114
+ return Array.isArray(data) ? data : []
115
+ } catch {
116
+ return []
117
+ }
118
+ }
119
+
120
+ onCleanup(() => abort.abort())
121
+
122
+ // Recursive Proxy: any sdk.client.X.Y.Z() call returns { data: [] } instead of crashing.
123
+ function makeProxy(overrides: Record<string, any> = {}): any {
124
+ const stub = async () => ({ data: [] })
125
+ return new Proxy(stub, {
126
+ get(_target, prop: string) {
127
+ if (prop in overrides) return overrides[prop]
128
+ return makeProxy()
129
+ },
130
+ apply(_target, _this, args) {
131
+ // Check if any override matches this call pattern
132
+ return Promise.resolve({ data: [] })
133
+ },
134
+ })
135
+ }
136
+
137
+ const client = makeProxy({
138
+ session: makeProxy({
139
+ createSession,
140
+ listSessions,
141
+ streamChat,
142
+ create: async (_opts: any) => ({ data: { id: await createSession() } }),
143
+ list: async (_opts: any) => ({ data: await listSessions() }),
144
+ get: async (_opts: any) => ({ data: undefined }),
145
+ delete: async (_opts: any) => ({ data: undefined }),
146
+ fork: async (_opts: any) => ({ data: undefined, error: new Error("not supported") }),
147
+ abort: async (_opts: any) => {},
148
+ prompt: async (_opts: any) => {},
149
+ shell: async (_opts: any) => {},
150
+ command: async (_opts: any) => {},
151
+ refresh: async () => {},
152
+ update: async (_opts: any) => ({ data: undefined }),
153
+ messages: async (_opts: any) => ({ data: [] }),
154
+ todo: async (_opts: any) => ({ data: [] }),
155
+ diff: async (_opts: any) => ({ data: [] }),
156
+ status: async (_opts: any) => ({ data: {} }),
157
+ }),
158
+ path: makeProxy({
159
+ get: async (_opts?: any) => ({
160
+ data: { home: "", state: "", config: "", worktree: "", directory: process.cwd() },
161
+ }),
162
+ }),
163
+ project: makeProxy({
164
+ current: async (_opts?: any) => ({ data: { id: props.pid } }),
165
+ }),
166
+ global: makeProxy({
167
+ upgrade: async (_opts: any) => ({ data: undefined, error: new Error("not supported") }),
168
+ }),
169
+ })
170
+
171
+ return {
172
+ url: props.url,
173
+ pid: props.pid,
174
+ agent: props.agent ?? "super-agent",
175
+ model: props.model ?? "claude-3-5-sonnet",
176
+ // Legacy opencode compat
177
+ directory: props.directory ?? process.cwd(),
178
+ event: emitter,
179
+ client,
180
+ streamChat,
181
+ createSession,
182
+ listSessions,
183
+ }
184
+ },
185
+ })
@@ -0,0 +1,6 @@
1
+ /**
2
+ * APX compatibility shim for the opencode SDK context.
3
+ * Re-exports from sdk-apx.tsx so existing imports continue to work.
4
+ */
5
+ export type { ApxEvent as EventSource } from "./sdk-apx"
6
+ export { useSDK, SDKProvider } from "./sdk-apx"
@@ -0,0 +1,178 @@
1
+ import { createStore, produce, reconcile } from "solid-js/store"
2
+ import { batch, onMount } from "solid-js"
3
+ import { createSimpleContext } from "./helper"
4
+ import { useSDK } from "./sdk-apx"
5
+ import type { ApxEvent } from "./sdk-apx"
6
+
7
+ export type ApxSession = {
8
+ id: string
9
+ title: string
10
+ updatedAt?: number
11
+ }
12
+
13
+ export type ApxMessage = {
14
+ id: string
15
+ sessionID: string
16
+ role: "user" | "assistant"
17
+ text: string
18
+ streaming?: boolean
19
+ error?: boolean
20
+ }
21
+
22
+ export const { use: useApxSync, provider: ApxSyncProvider } = createSimpleContext({
23
+ name: "ApxSync",
24
+ init: () => {
25
+ const sdk = useSDK()
26
+
27
+ const [store, setStore] = createStore<{
28
+ status: "loading" | "ready"
29
+ sessions: ApxSession[]
30
+ messages: Record<string, ApxMessage[]>
31
+ currentSessionID: string | undefined
32
+ previousMessages: Array<{ role: string; content: string }>
33
+ }>({
34
+ status: "loading",
35
+ sessions: [],
36
+ messages: {},
37
+ currentSessionID: undefined,
38
+ previousMessages: [],
39
+ })
40
+
41
+ // Listen to APX stream events
42
+ sdk.event.on("event", (ev: ApxEvent) => {
43
+ if (ev.type === "chunk") {
44
+ const e = ev
45
+ setStore(
46
+ "messages",
47
+ produce((draft) => {
48
+ const msgs = (draft[e.sessionID] ??= [])
49
+ const last = msgs[msgs.length - 1]
50
+ if (last?.role === "assistant" && last.streaming) {
51
+ last.text += e.chunk
52
+ } else {
53
+ msgs.push({
54
+ id: `msg-${Date.now()}`,
55
+ sessionID: e.sessionID,
56
+ role: "assistant",
57
+ text: e.chunk,
58
+ streaming: true,
59
+ })
60
+ }
61
+ }),
62
+ )
63
+ }
64
+
65
+ if (ev.type === "final") {
66
+ const e = ev
67
+ setStore(
68
+ "messages",
69
+ produce((draft) => {
70
+ const msgs = (draft[e.sessionID] ??= [])
71
+ const last = msgs[msgs.length - 1]
72
+ if (last?.role === "assistant") {
73
+ last.text = e.text
74
+ last.streaming = false
75
+ }
76
+ }),
77
+ )
78
+ setStore("previousMessages", (prev) => [...prev, { role: "assistant", content: e.text }])
79
+ }
80
+
81
+ if (ev.type === "error") {
82
+ const e = ev
83
+ setStore(
84
+ "messages",
85
+ produce((draft) => {
86
+ const msgs = (draft[e.sessionID] ??= [])
87
+ const last = msgs[msgs.length - 1]
88
+ if (last?.role === "assistant" && last.streaming) {
89
+ last.streaming = false
90
+ last.error = true
91
+ } else {
92
+ msgs.push({
93
+ id: `err-${Date.now()}`,
94
+ sessionID: e.sessionID,
95
+ role: "assistant",
96
+ text: e.error,
97
+ streaming: false,
98
+ error: true,
99
+ })
100
+ }
101
+ }),
102
+ )
103
+ }
104
+ })
105
+
106
+ async function loadSessions() {
107
+ const list = await sdk.listSessions()
108
+ setStore("sessions", reconcile(list))
109
+ }
110
+
111
+ async function ensureSession(): Promise<string> {
112
+ if (store.currentSessionID) return store.currentSessionID
113
+ const id = await sdk.createSession()
114
+ setStore("currentSessionID", id)
115
+ setStore(
116
+ "sessions",
117
+ produce((draft) => {
118
+ draft.unshift({ id, title: "New session" })
119
+ }),
120
+ )
121
+ return id
122
+ }
123
+
124
+ async function sendMessage(text: string) {
125
+ const sessionID = await ensureSession()
126
+ const userMsg: ApxMessage = {
127
+ id: `user-${Date.now()}`,
128
+ sessionID,
129
+ role: "user",
130
+ text,
131
+ }
132
+ batch(() => {
133
+ setStore(
134
+ "messages",
135
+ produce((draft) => {
136
+ ;(draft[sessionID] ??= []).push(userMsg)
137
+ }),
138
+ )
139
+ setStore("previousMessages", (prev) => [...prev, { role: "user", content: text }])
140
+ })
141
+ await sdk.streamChat(sessionID, text, store.previousMessages.slice(-20))
142
+ }
143
+
144
+ onMount(() => {
145
+ void loadSessions().finally(() => setStore("status", "ready"))
146
+ })
147
+
148
+ return {
149
+ data: store,
150
+ get status() {
151
+ return store.status
152
+ },
153
+ get ready() {
154
+ return store.status === "ready"
155
+ },
156
+ session: {
157
+ get(sessionID: string) {
158
+ return store.sessions.find((s) => s.id === sessionID)
159
+ },
160
+ list() {
161
+ return store.sessions
162
+ },
163
+ current() {
164
+ return store.currentSessionID
165
+ },
166
+ setCurrent(id: string) {
167
+ setStore("currentSessionID", id)
168
+ },
169
+ messages(sessionID: string) {
170
+ return store.messages[sessionID] ?? []
171
+ },
172
+ async refresh() {},
173
+ },
174
+ sendMessage,
175
+ ensureSession,
176
+ }
177
+ },
178
+ })
@@ -0,0 +1,16 @@
1
+ /**
2
+ * APX compatibility shim for sync-v2.tsx.
3
+ * The original file tracked opencode-specific V2 session messages.
4
+ * For APX we don't need it — provide a no-op provider.
5
+ */
6
+ import { createContext, useContext, type ParentProps } from "solid-js"
7
+
8
+ const ctx = createContext<Record<string, never>>({})
9
+
10
+ export function SyncProviderV2(props: ParentProps) {
11
+ return <ctx.Provider value={{}}>{props.children}</ctx.Provider>
12
+ }
13
+
14
+ export function useSyncV2() {
15
+ return useContext(ctx)
16
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * APX compatibility shim for the opencode Sync context.
3
+ *
4
+ * This file replaces the original opencode sync.tsx with a version that delegates
5
+ * to the APX sync context (sync-apx.tsx) while providing the same interface shape
6
+ * that other TUI components expect.
7
+ */
8
+ import { createSimpleContext } from "./helper"
9
+ import { useApxSync } from "./sync-apx"
10
+ import { onMount } from "solid-js"
11
+
12
+ // Re-export useApxSync as useSync for compatibility
13
+ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
14
+ name: "Sync",
15
+ init: () => {
16
+ const apx = useApxSync()
17
+
18
+ onMount(() => {
19
+ // APX sync already loads sessions in its own onMount
20
+ })
21
+
22
+ // Return a compatible object that matches the shape expected by existing TUI components
23
+ return {
24
+ data: {
25
+ get status() {
26
+ return apx.status
27
+ },
28
+ // Provider fields (APX has no providers — return stubs)
29
+ provider: [] as any[],
30
+ provider_default: {} as Record<string, string>,
31
+ provider_next: { all: [], default: {}, connected: [] } as any,
32
+ provider_auth: {} as Record<string, any[]>,
33
+ console_state: { switchableOrgCount: 0 } as any,
34
+ // Agent fields — empty for APX (agent is configured via CLI args)
35
+ agent: [] as any[],
36
+ command: [] as any[],
37
+ // Session-related — delegate to APX
38
+ get session() {
39
+ return apx.session.list().map((s: any) => ({
40
+ ...s,
41
+ time: { updated: s.updatedAt ?? Date.now(), compacting: false },
42
+ cost: 0,
43
+ workspaceID: undefined,
44
+ parentID: undefined,
45
+ }))
46
+ },
47
+ session_status: {} as Record<string, any>,
48
+ session_diff: {} as Record<string, any[]>,
49
+ // Messages — delegate to APX
50
+ get message() {
51
+ return apx.data.messages as Record<string, any[]>
52
+ },
53
+ messages: {} as Record<string, any[]>,
54
+ part: {} as Record<string, any[]>,
55
+ // Permission/question stubs
56
+ permission: {} as Record<string, any[]>,
57
+ question: {} as Record<string, any[]>,
58
+ todo: {} as Record<string, any[]>,
59
+ // Config stub
60
+ config: {
61
+ experimental: {
62
+ disable_paste_summary: false,
63
+ },
64
+ model: undefined,
65
+ plugin: [],
66
+ reference: [],
67
+ share: undefined,
68
+ } as any,
69
+ // LSP/MCP stubs
70
+ lsp: [] as any[],
71
+ mcp: {} as Record<string, any>,
72
+ mcp_resource: {} as Record<string, any>,
73
+ formatter: [] as any[],
74
+ vcs: undefined as any,
75
+ },
76
+
77
+ get status() {
78
+ return apx.status === "ready" ? "complete" : apx.status === "loading" ? "loading" : "partial"
79
+ },
80
+
81
+ get ready() {
82
+ return apx.ready
83
+ },
84
+
85
+ session: {
86
+ get(sessionID: string) {
87
+ const s = apx.session.get(sessionID)
88
+ if (!s) return undefined
89
+ return {
90
+ ...s,
91
+ time: { updated: s.updatedAt ?? Date.now(), compacting: false },
92
+ cost: 0,
93
+ workspaceID: undefined,
94
+ parentID: undefined,
95
+ }
96
+ },
97
+ query() {
98
+ return {}
99
+ },
100
+ async refresh() {
101
+ await apx.session.refresh()
102
+ },
103
+ status(_sessionID: string) {
104
+ return "idle" as const
105
+ },
106
+ async sync(_sessionID: string) {},
107
+ },
108
+
109
+ async bootstrap() {},
110
+
111
+ // Path info for autocomplete and other components
112
+ path: {
113
+ directory: process.cwd(),
114
+ worktree: process.cwd(),
115
+ },
116
+ }
117
+ },
118
+ })
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://opencode.ai/theme.json",
3
+ "defs": {
4
+ "darkBg": "#0f0f0f",
5
+ "darkBgPanel": "#15141b",
6
+ "darkBorder": "#2d2d2d",
7
+ "darkFgMuted": "#6d6d6d",
8
+ "darkFg": "#edecee",
9
+ "purple": "#a277ff",
10
+ "pink": "#f694ff",
11
+ "blue": "#82e2ff",
12
+ "red": "#ff6767",
13
+ "orange": "#ffca85",
14
+ "cyan": "#61ffca",
15
+ "green": "#9dff65"
16
+ },
17
+ "theme": {
18
+ "primary": "purple",
19
+ "secondary": "pink",
20
+ "accent": "purple",
21
+ "error": "red",
22
+ "warning": "orange",
23
+ "success": "cyan",
24
+ "info": "purple",
25
+ "text": "darkFg",
26
+ "textMuted": "darkFgMuted",
27
+ "background": "darkBg",
28
+ "backgroundPanel": "darkBgPanel",
29
+ "backgroundElement": "darkBgPanel",
30
+ "border": "darkBorder",
31
+ "borderActive": "darkFgMuted",
32
+ "borderSubtle": "darkBorder",
33
+ "diffAdded": "cyan",
34
+ "diffRemoved": "red",
35
+ "diffContext": "darkFgMuted",
36
+ "diffHunkHeader": "darkFgMuted",
37
+ "diffHighlightAdded": "cyan",
38
+ "diffHighlightRemoved": "red",
39
+ "diffAddedBg": "#354933",
40
+ "diffRemovedBg": "#3f191a",
41
+ "diffContextBg": "darkBgPanel",
42
+ "diffLineNumber": "#898989",
43
+ "diffAddedLineNumberBg": "#162620",
44
+ "diffRemovedLineNumberBg": "#26161a",
45
+ "markdownText": "darkFg",
46
+ "markdownHeading": "purple",
47
+ "markdownLink": "pink",
48
+ "markdownLinkText": "purple",
49
+ "markdownCode": "cyan",
50
+ "markdownBlockQuote": "darkFgMuted",
51
+ "markdownEmph": "orange",
52
+ "markdownStrong": "purple",
53
+ "markdownHorizontalRule": "darkFgMuted",
54
+ "markdownListItem": "purple",
55
+ "markdownListEnumeration": "purple",
56
+ "markdownImage": "pink",
57
+ "markdownImageText": "purple",
58
+ "markdownCodeBlock": "darkFg",
59
+ "syntaxComment": "darkFgMuted",
60
+ "syntaxKeyword": "pink",
61
+ "syntaxFunction": "purple",
62
+ "syntaxVariable": "purple",
63
+ "syntaxString": "cyan",
64
+ "syntaxNumber": "green",
65
+ "syntaxType": "purple",
66
+ "syntaxOperator": "pink",
67
+ "syntaxPunctuation": "darkFg"
68
+ }
69
+ }