@agentprojectcontext/apx 1.15.6 → 1.16.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/package.json +40 -5
- package/src/cli/commands/log.js +113 -0
- package/src/cli/commands/overlay.js +253 -0
- package/src/cli/commands/sys.js +88 -16
- package/src/cli/index.js +23 -1
- package/src/cli/terminal-chat/renderer.js +71 -56
- package/src/cli-ts/commands/agent.ts +173 -0
- package/src/cli-ts/commands/chat.ts +119 -0
- package/src/cli-ts/commands/daemon.ts +112 -0
- package/src/cli-ts/commands/exec.ts +109 -0
- package/src/cli-ts/commands/mcp.ts +235 -0
- package/src/cli-ts/commands/session.ts +224 -0
- package/src/cli-ts/commands/status.ts +61 -0
- package/src/cli-ts/http.ts +36 -0
- package/src/cli-ts/index.ts +73 -0
- package/src/cli-ts/ui.ts +107 -0
- package/src/core/logging.js +81 -0
- package/src/daemon/api.js +58 -0
- package/src/daemon/engines/anthropic.js +60 -1
- package/src/daemon/engines/index.js +2 -1
- package/src/daemon/engines/ollama.js +70 -3
- package/src/daemon/index.js +58 -0
- package/src/daemon/overlay-ws.js +40 -0
- package/src/daemon/plugins/index.js +2 -1
- package/src/daemon/plugins/overlay.js +177 -0
- package/src/daemon/plugins/telegram.js +15 -3
- package/src/daemon/super-agent.js +102 -19
- package/src/daemon/transcription.js +262 -59
- package/src/daemon/whisper-server.py +57 -6
- package/src/overlay/index.html +44 -0
- package/src/overlay/main.js +480 -0
- package/src/overlay/package.json +3 -0
- package/src/overlay/preload.js +34 -0
- package/src/overlay/renderer.js +371 -0
- package/src/overlay/style.css +250 -0
- package/src/tui/_shims/cli-error.ts +6 -0
- package/src/tui/_shims/cli-logo.ts +18 -0
- package/src/tui/_shims/cli-ui.ts +1 -0
- package/src/tui/_shims/config-console-state.ts +7 -0
- package/src/tui/_shims/core-any.ts +30 -0
- package/src/tui/_shims/core-binary.ts +13 -0
- package/src/tui/_shims/core-flag.ts +3 -0
- package/src/tui/_shims/core-log.ts +14 -0
- package/src/tui/_shims/lsp-language.ts +1 -0
- package/src/tui/_shims/opencode-any.ts +135 -0
- package/src/tui/_shims/opencode-sdk-v2.ts +48 -0
- package/src/tui/_shims/plugin-tui.ts +13 -0
- package/src/tui/_shims/provider-provider.ts +10 -0
- package/src/tui/_shims/session-retry.ts +1 -0
- package/src/tui/_shims/session-schema.ts +15 -0
- package/src/tui/_shims/session-session.ts +3 -0
- package/src/tui/_shims/snapshot.ts +4 -0
- package/src/tui/_shims/tool-any.ts +18 -0
- package/src/tui/_shims/util-error.ts +7 -0
- package/src/tui/_shims/util-filesystem.ts +79 -0
- package/src/tui/_shims/util-format.ts +7 -0
- package/src/tui/_shims/util-iife.ts +3 -0
- package/src/tui/_shims/util-locale.ts +10 -0
- package/src/tui/_shims/util-process.ts +38 -0
- package/src/tui/app.tsx +783 -0
- package/src/tui/asset/charge.wav +0 -0
- package/src/tui/asset/pulse-a.wav +0 -0
- package/src/tui/asset/pulse-b.wav +0 -0
- package/src/tui/asset/pulse-c.wav +0 -0
- package/src/tui/attach.ts +100 -0
- package/src/tui/component/bg-pulse-render.ts +436 -0
- package/src/tui/component/bg-pulse.tsx +99 -0
- package/src/tui/component/border.tsx +21 -0
- package/src/tui/component/dialog-agent.tsx +31 -0
- package/src/tui/component/dialog-console-org.tsx +103 -0
- package/src/tui/component/dialog-mcp.tsx +85 -0
- package/src/tui/component/dialog-model.tsx +175 -0
- package/src/tui/component/dialog-provider.tsx +456 -0
- package/src/tui/component/dialog-retry-action.tsx +160 -0
- package/src/tui/component/dialog-session-delete-failed.tsx +99 -0
- package/src/tui/component/dialog-session-list.tsx +323 -0
- package/src/tui/component/dialog-session-rename.tsx +31 -0
- package/src/tui/component/dialog-skill.tsx +36 -0
- package/src/tui/component/dialog-stash.tsx +87 -0
- package/src/tui/component/dialog-status.tsx +168 -0
- package/src/tui/component/dialog-tag.tsx +44 -0
- package/src/tui/component/dialog-theme-list.tsx +50 -0
- package/src/tui/component/dialog-variant.tsx +39 -0
- package/src/tui/component/dialog-workspace-create.tsx +302 -0
- package/src/tui/component/dialog-workspace-file-changes.tsx +138 -0
- package/src/tui/component/dialog-workspace-unavailable.tsx +69 -0
- package/src/tui/component/error-component.tsx +92 -0
- package/src/tui/component/logo.tsx +896 -0
- package/src/tui/component/plugin-route-missing.tsx +14 -0
- package/src/tui/component/prompt/autocomplete.tsx +869 -0
- package/src/tui/component/prompt/cwd.ts +0 -0
- package/src/tui/component/prompt/frecency.tsx +90 -0
- package/src/tui/component/prompt/history.tsx +108 -0
- package/src/tui/component/prompt/index.tsx +1809 -0
- package/src/tui/component/prompt/part.ts +16 -0
- package/src/tui/component/prompt/stash.tsx +101 -0
- package/src/tui/component/prompt/traits.ts +35 -0
- package/src/tui/component/spinner.tsx +24 -0
- package/src/tui/component/startup-loading.tsx +63 -0
- package/src/tui/component/todo-item.tsx +32 -0
- package/src/tui/component/use-connected.tsx +9 -0
- package/src/tui/component/workspace-label.tsx +19 -0
- package/src/tui/config/cwd.ts +5 -0
- package/src/tui/config/keybind.ts +432 -0
- package/src/tui/config/tui-migrate.ts +154 -0
- package/src/tui/config/tui-schema.ts +34 -0
- package/src/tui/config/tui.ts +46 -0
- package/src/tui/context/aggregate-failures.ts +34 -0
- package/src/tui/context/args.tsx +15 -0
- package/src/tui/context/command-palette.tsx +163 -0
- package/src/tui/context/directory.ts +15 -0
- package/src/tui/context/editor-zed.ts +283 -0
- package/src/tui/context/editor.ts +468 -0
- package/src/tui/context/event-apx.ts +22 -0
- package/src/tui/context/event.ts +6 -0
- package/src/tui/context/exit.tsx +60 -0
- package/src/tui/context/helper.tsx +25 -0
- package/src/tui/context/kv.tsx +81 -0
- package/src/tui/context/local.tsx +608 -0
- package/src/tui/context/path-format.tsx +39 -0
- package/src/tui/context/project-apx.tsx +48 -0
- package/src/tui/context/project.tsx +7 -0
- package/src/tui/context/prompt.tsx +18 -0
- package/src/tui/context/route.tsx +52 -0
- package/src/tui/context/sdk-apx.tsx +185 -0
- package/src/tui/context/sdk.tsx +6 -0
- package/src/tui/context/sync-apx.tsx +178 -0
- package/src/tui/context/sync-v2.tsx +16 -0
- package/src/tui/context/sync.tsx +118 -0
- package/src/tui/context/theme/aura.json +69 -0
- package/src/tui/context/theme/ayu.json +80 -0
- package/src/tui/context/theme/carbonfox.json +248 -0
- package/src/tui/context/theme/catppuccin-frappe.json +230 -0
- package/src/tui/context/theme/catppuccin-macchiato.json +230 -0
- package/src/tui/context/theme/catppuccin.json +112 -0
- package/src/tui/context/theme/cobalt2.json +225 -0
- package/src/tui/context/theme/cursor.json +249 -0
- package/src/tui/context/theme/dracula.json +219 -0
- package/src/tui/context/theme/everforest.json +241 -0
- package/src/tui/context/theme/flexoki.json +237 -0
- package/src/tui/context/theme/github.json +233 -0
- package/src/tui/context/theme/gruvbox.json +242 -0
- package/src/tui/context/theme/kanagawa.json +77 -0
- package/src/tui/context/theme/lucent-orng.json +234 -0
- package/src/tui/context/theme/material.json +235 -0
- package/src/tui/context/theme/matrix.json +77 -0
- package/src/tui/context/theme/mercury.json +252 -0
- package/src/tui/context/theme/monokai.json +221 -0
- package/src/tui/context/theme/nightowl.json +221 -0
- package/src/tui/context/theme/nord.json +223 -0
- package/src/tui/context/theme/one-dark.json +84 -0
- package/src/tui/context/theme/opencode.json +245 -0
- package/src/tui/context/theme/orng.json +249 -0
- package/src/tui/context/theme/osaka-jade.json +93 -0
- package/src/tui/context/theme/palenight.json +222 -0
- package/src/tui/context/theme/rosepine.json +234 -0
- package/src/tui/context/theme/solarized.json +223 -0
- package/src/tui/context/theme/synthwave84.json +226 -0
- package/src/tui/context/theme/tokyonight.json +243 -0
- package/src/tui/context/theme/vercel.json +245 -0
- package/src/tui/context/theme/vesper.json +218 -0
- package/src/tui/context/theme/zenburn.json +223 -0
- package/src/tui/context/theme.tsx +1247 -0
- package/src/tui/context/tui-config.tsx +9 -0
- package/src/tui/event.ts +16 -0
- package/src/tui/feature-plugins/home/footer.tsx +94 -0
- package/src/tui/feature-plugins/home/tips-view.tsx +166 -0
- package/src/tui/feature-plugins/home/tips.tsx +59 -0
- package/src/tui/feature-plugins/sidebar/context.tsx +65 -0
- package/src/tui/feature-plugins/sidebar/files.tsx +63 -0
- package/src/tui/feature-plugins/sidebar/footer.tsx +94 -0
- package/src/tui/feature-plugins/sidebar/lsp.tsx +65 -0
- package/src/tui/feature-plugins/sidebar/mcp.tsx +97 -0
- package/src/tui/feature-plugins/sidebar/todo.tsx +49 -0
- package/src/tui/feature-plugins/system/plugins.tsx +269 -0
- package/src/tui/feature-plugins/system/session-v2.tsx +1143 -0
- package/src/tui/feature-plugins/system/which-key.tsx +608 -0
- package/src/tui/keymap.tsx +166 -0
- package/src/tui/layer.ts +6 -0
- package/src/tui/plugin/api.tsx +381 -0
- package/src/tui/plugin/command-shim.ts +109 -0
- package/src/tui/plugin/internal.ts +33 -0
- package/src/tui/plugin/runtime.ts +1069 -0
- package/src/tui/plugin/slots.tsx +60 -0
- package/src/tui/routes/home.tsx +96 -0
- package/src/tui/routes/session/dialog-fork-from-timeline.tsx +76 -0
- package/src/tui/routes/session/dialog-message.tsx +108 -0
- package/src/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/tui/routes/session/footer.tsx +91 -0
- package/src/tui/routes/session/index.tsx +188 -0
- package/src/tui/routes/session/permission.tsx +722 -0
- package/src/tui/routes/session/question.tsx +490 -0
- package/src/tui/routes/session/sidebar.tsx +102 -0
- package/src/tui/routes/session/subagent-footer.tsx +133 -0
- package/src/tui/run.ts +84 -0
- package/src/tui/thread.ts +261 -0
- package/src/tui/tsconfig.json +40 -0
- package/src/tui/ui/dialog-alert.tsx +66 -0
- package/src/tui/ui/dialog-confirm.tsx +108 -0
- package/src/tui/ui/dialog-export-options.tsx +217 -0
- package/src/tui/ui/dialog-help.tsx +40 -0
- package/src/tui/ui/dialog-prompt.tsx +101 -0
- package/src/tui/ui/dialog-select.tsx +553 -0
- package/src/tui/ui/dialog.tsx +211 -0
- package/src/tui/ui/link.tsx +34 -0
- package/src/tui/ui/spinner.ts +368 -0
- package/src/tui/ui/toast.tsx +111 -0
- package/src/tui/util/clipboard.ts +217 -0
- package/src/tui/util/editor.ts +37 -0
- package/src/tui/util/model.ts +23 -0
- package/src/tui/util/provider-origin.ts +7 -0
- package/src/tui/util/revert-diff.ts +18 -0
- package/src/tui/util/scroll.ts +25 -0
- package/src/tui/util/selection.ts +65 -0
- package/src/tui/util/signal.ts +41 -0
- package/src/tui/util/sound.ts +156 -0
- package/src/tui/util/transcript.ts +112 -0
- package/src/tui/validate-session.ts +29 -0
- package/src/tui/win32.ts +130 -0
- 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,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
|
+
}
|