@pmcai/codex-chat-widget 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Suphot (pemo)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @pmcai/codex-chat-widget
2
+
3
+ Embeddable React chat UI for a **Codex harness** server (the Next.js + `codex exec` service
4
+ in this repo). Drop it into any React / Next.js / Vite site and it drives the existing API:
5
+
6
+ ```
7
+ GET /api/health
8
+ GET /api/sessions POST /api/sessions PATCH/DELETE /api/sessions?id=
9
+ GET /api/settings PUT /api/settings
10
+ POST /api/models
11
+ POST /api/chat (SSE stream: status / final / error)
12
+ ```
13
+
14
+ The widget is **frontend only**: it never holds API keys and never runs Codex itself.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @pmcai/codex-chat-widget
20
+ ```
21
+
22
+ Peer dependencies (install in your app):
23
+
24
+ ```bash
25
+ npm install react react-dom react-markdown remark-gfm
26
+ ```
27
+
28
+ ## Use
29
+
30
+ ```jsx
31
+ import CodexChatWidget from '@pmcai/codex-chat-widget'
32
+ import '@pmcai/codex-chat-widget/styles.css'
33
+
34
+ export default function Page() {
35
+ return (
36
+ <div style={{ height: '100dvh' }}>
37
+ <CodexChatWidget
38
+ apiBase="https://f90f-124-120-29-18.ngrok-free.app"
39
+ storageKey="my-site"
40
+ title="Codex Harness"
41
+ />
42
+ </div>
43
+ )
44
+ }
45
+ ```
46
+
47
+ Without a bundler (CDN / plain HTML) — see `examples/static.html` in this repo:
48
+
49
+ ```html
50
+ <link rel="stylesheet" href="https://unpkg.com/@pmcai/codex-chat-widget/dist/styles.css" />
51
+ <script type="module">
52
+ import CodexChatWidget from 'https://unpkg.com/@pmcai/codex-chat-widget/dist/index.js'
53
+ // mount with your own React root
54
+ </script>
55
+ ```
56
+
57
+ ## Props
58
+
59
+ | Prop | Type | Default | Description |
60
+ |------|------|---------|-------------|
61
+ | `apiBase` | `string` | `''` (same origin) | Base URL of the Codex harness server. Trailing `/` is stripped. |
62
+ | `storageKey` | `string` | `'codex-chat'` | Namespace for the remembered active session in `localStorage`. Use a unique value per host site. |
63
+ | `initialSessionId` | `string` | — | Session to open on first mount (overrides the remembered one). |
64
+ | `title` | `string` | `'Codex Harness'` | Header title. |
65
+
66
+ ## Behaviour
67
+
68
+ - **Parallel sessions** — each session streams independently; starting a new chat does not
69
+ block a running one. Progress steps render per session.
70
+ - **Session memory** — the active session id is stored under
71
+ `<storageKey>-active-session`, scoped per host site.
72
+ - **Model settings** — the Settings modal reads providers/models from `/api/models` and saves
73
+ globally through `PUT /api/settings`.
74
+ - **Styles** are fully scoped under `.codex-chat-app`, so the widget will not leak CSS into
75
+ your site. Global `html`/`body` resets from the standalone app are not included.
76
+
77
+ ## Server contract
78
+
79
+ The widget expects the harness API to answer with these shapes:
80
+
81
+ ```jsonc
82
+ // GET /api/sessions -> [{ id, title, updatedAt }]
83
+ // GET /api/sessions?id=... -> { id, title, messages: [{ role: "user"|"assistant"|"system", text }] }
84
+ // POST /api/chat { sessionId, prompt, settings } -> text/event-stream
85
+ // data: {"type":"status","text":"..."} // progress step
86
+ // data: {"type":"final","text":"..."} // final answer
87
+ // data: {"type":"error","text":"..."} // failure
88
+ ```
89
+
90
+ ## Build from source
91
+
92
+ ```bash
93
+ npm run build # esbuild -> dist/index.js, dist/index.cjs, dist/styles.css
94
+ npm test # react-dom/server smoke render
95
+ ```
96
+
97
+ ## Publish
98
+
99
+ ```bash
100
+ npm login
101
+ npm publish --access public
102
+ ```
103
+
104
+ ## License
105
+
106
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,276 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/CodexChatWidget.jsx
30
+ var CodexChatWidget_exports = {};
31
+ __export(CodexChatWidget_exports, {
32
+ default: () => CodexChatWidget
33
+ });
34
+ module.exports = __toCommonJS(CodexChatWidget_exports);
35
+ var import_react = require("react");
36
+ var import_react_markdown = __toESM(require("react-markdown"), 1);
37
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var defaultSettings = { provider: "server", model: "cohere/north-mini-code:free", apiKey: "", baseUrl: "" };
40
+ function CodexChatWidget({
41
+ apiBase = "",
42
+ storageKey = "codex-chat",
43
+ initialSessionId,
44
+ title = "Codex Harness"
45
+ }) {
46
+ const apiBaseUrl = String(apiBase || "").replace(/\/$/, "");
47
+ const [sessions, setSessions] = (0, import_react.useState)([]);
48
+ const [session, setSession] = (0, import_react.useState)(null);
49
+ const [prompt, setPrompt] = (0, import_react.useState)("");
50
+ const [activityBySession, setActivityBySession] = (0, import_react.useState)({});
51
+ const [runningBySession, setRunningBySession] = (0, import_react.useState)({});
52
+ const [settings, setSettings] = (0, import_react.useState)(defaultSettings);
53
+ const [models, setModels] = (0, import_react.useState)([]);
54
+ const [modelsState, setModelsState] = (0, import_react.useState)("");
55
+ const [showSettings, setShowSettings] = (0, import_react.useState)(false);
56
+ const [showSessions, setShowSessions] = (0, import_react.useState)(false);
57
+ const [homeLabel, setHomeLabel] = (0, import_react.useState)("~/.pmai/workspace");
58
+ async function refreshSessions(selectId) {
59
+ let items = await (await fetch(apiBaseUrl + "/api/sessions")).json();
60
+ if (!items.length) {
61
+ const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
62
+ items = [created];
63
+ }
64
+ setSessions(items);
65
+ const wanted = selectId || session?.id;
66
+ const id = items.some((item) => item.id === wanted) ? wanted : items[0].id;
67
+ const response = await fetch(apiBaseUrl + `/api/sessions?id=${id}`);
68
+ if (response.ok) {
69
+ setSession(await response.json());
70
+ localStorage.setItem(`${storageKey}-active-session`, id);
71
+ }
72
+ }
73
+ (0, import_react.useEffect)(() => {
74
+ const savedSession = localStorage.getItem(`${storageKey}-active-session`);
75
+ refreshSessions(initialSessionId || savedSession || void 0);
76
+ fetch(apiBaseUrl + "/api/settings").then((r) => r.json()).then(setSettings);
77
+ fetch(apiBaseUrl + "/api/health").then((r) => r.json()).then((h) => {
78
+ if (h.workspace) setHomeLabel("~/.pmai" + String(h.workspace).replace(/^\/pmai/, ""));
79
+ }).catch(() => {
80
+ });
81
+ }, []);
82
+ async function loadModels(nextSettings = settings) {
83
+ setModelsState("\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 model list\u2026");
84
+ const data = await (await fetch(apiBaseUrl + "/api/models", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(nextSettings), cache: "no-store" })).json();
85
+ setModels(data.models || []);
86
+ setModelsState(data.error || data.message || data.label || `${(data.models || []).length} models`);
87
+ }
88
+ async function openSettings() {
89
+ setShowSettings(true);
90
+ await loadModels();
91
+ }
92
+ async function saveGlobalSettings() {
93
+ const previous = `${settings.provider}/${settings.model}`;
94
+ const saved = await (await fetch(apiBaseUrl + "/api/settings", { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify(settings) })).json();
95
+ setSettings(saved);
96
+ if (session && `${saved.provider}/${saved.model}` !== previous) setSession((current) => ({ ...current, messages: [...current.messages, { role: "system", text: `Model changed: ${saved.model} (${saved.provider})` }] }));
97
+ setShowSettings(false);
98
+ }
99
+ async function newSession() {
100
+ const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
101
+ await refreshSessions(created.id);
102
+ }
103
+ async function choose(id) {
104
+ await refreshSessions(id);
105
+ }
106
+ async function archive(id) {
107
+ await fetch(apiBaseUrl + "/api/sessions", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ id, archived: true }) });
108
+ await refreshSessions();
109
+ }
110
+ async function remove(id) {
111
+ if (!confirm("Delete this chat permanently?")) return;
112
+ await fetch(apiBaseUrl + `/api/sessions?id=${id}`, { method: "DELETE" });
113
+ if (session?.id === id) setSession(null);
114
+ await refreshSessions();
115
+ }
116
+ async function send(event) {
117
+ event.preventDefault();
118
+ const text = prompt.trim();
119
+ const sessionId = session?.id;
120
+ if (!text || runningBySession[sessionId] || !sessionId) return;
121
+ setSession((x) => ({ ...x, messages: [...x.messages, { role: "user", text }] }));
122
+ setPrompt("");
123
+ setActivityBySession((x) => ({ ...x, [sessionId]: [{ text: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E15\u0E23\u0E35\u0E22\u0E21\u0E07\u0E32\u0E19" }] }));
124
+ setRunningBySession((x) => ({ ...x, [sessionId]: true }));
125
+ try {
126
+ const response = await fetch(apiBaseUrl + "/api/chat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ prompt: text, sessionId, settings }) });
127
+ if (!response.ok || !response.body) {
128
+ const error = await response.json();
129
+ throw new Error(error.error || `Request failed (${response.status})`);
130
+ }
131
+ const reader = response.body.getReader();
132
+ const decoder = new TextDecoder();
133
+ let buffer = "";
134
+ let finalText = "";
135
+ while (true) {
136
+ const { done, value } = await reader.read();
137
+ if (done) break;
138
+ buffer += decoder.decode(value, { stream: true });
139
+ const frames = buffer.split("\n\n");
140
+ buffer = frames.pop() || "";
141
+ for (const frame of frames) {
142
+ const line = frame.split("\n").find((x) => x.startsWith("data: "));
143
+ if (!line) continue;
144
+ const data = JSON.parse(line.slice(6));
145
+ if (data.type === "status") setActivityBySession((x) => ({ ...x, [sessionId]: [...x[sessionId] || [], { text: data.text }] }));
146
+ if (data.type === "final") finalText = data.text;
147
+ if (data.type === "error") throw new Error(data.text);
148
+ }
149
+ }
150
+ if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: "assistant", text: finalText || "\u0E07\u0E32\u0E19\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E41\u0E25\u0E49\u0E27 \u0E41\u0E15\u0E48\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E2A\u0E23\u0E38\u0E1B" }] }));
151
+ await refreshSessions(sessionId);
152
+ } catch (error) {
153
+ if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: "assistant", text: `\u0E40\u0E01\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14: ${error.message}` }] }));
154
+ } finally {
155
+ setActivityBySession((x) => ({ ...x, [sessionId]: [] }));
156
+ setRunningBySession((x) => ({ ...x, [sessionId]: false }));
157
+ }
158
+ }
159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { className: "codex-chat-app", children: [
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", { className: "codex-chat-sidebar", children: [
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-new", onClick: newSession, children: "\uFF0B New chat" }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionRow", children: [
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: () => choose(item.id), children: [
164
+ item.title,
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: new Date(item.updatedAt).toLocaleString() })
166
+ ] }),
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionActions", children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { title: "Archive", onClick: () => archive(item.id), children: "\u232B" }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { title: "Delete", onClick: () => remove(item.id), children: "\xD7" })
170
+ ] })
171
+ ] }, item.id)) }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-settingsBtn", onClick: openSettings, children: "\u2699 Settings" })
173
+ ] }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "codex-chat-shell", children: [
175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: title }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
179
+ settings.model,
180
+ " \xB7 ",
181
+ homeLabel
182
+ ] })
183
+ ] }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-headerActions", children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-sessionsBtn", onClick: () => setShowSessions(true), children: "\u2630 Chats" }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-modelBtn", onClick: openSettings, children: "\u2304 Model" }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "codex-chat-status", children: "\u25CF Ready" })
188
+ ] })
189
+ ] }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "codex-chat-messages", "aria-live": "polite", children: !session ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-welcome", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 session\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
191
+ session.messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-welcome", children: [
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E38\u0E22\u0E01\u0E31\u0E1A Codex" }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "\u0E40\u0E25\u0E37\u0E2D\u0E01 model \u0E2B\u0E23\u0E37\u0E2D provider \u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13\u0E44\u0E14\u0E49\u0E08\u0E32\u0E01 Settings" })
194
+ ] }),
195
+ session.messages.map((message, index) => message.role === "system" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-systemMessage", children: message.text }, index) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: message.role, children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: message.role === "user" ? "\u0E04\u0E38\u0E13" : "\u0E04\u0E33\u0E15\u0E2D\u0E1A" }),
197
+ message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-markdown", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], children: message.text }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { children: message.text })
198
+ ] }, index)),
199
+ runningBySession[session.id] && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "codex-chat-assistant progress", children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19" }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { children: (activityBySession[session.id] || []).map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: index === (activityBySession[session.id] || []).length - 1 ? "active" : "", children: item.text }, index)) })
202
+ ] })
203
+ ] }) }),
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-composer", children: [
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-modelFooter", children: [
206
+ settings.provider,
207
+ " \xB7 ",
208
+ settings.model
209
+ ] }),
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: send, children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { value: prompt, onChange: (e) => setPrompt(e.target.value), placeholder: "\u0E40\u0E0A\u0E48\u0E19 \u0E15\u0E23\u0E27\u0E08\u0E44\u0E1F\u0E25\u0E4C\u0E43\u0E19 workspace \u0E41\u0E25\u0E49\u0E27\u0E2A\u0E23\u0E38\u0E1B\u0E42\u0E04\u0E23\u0E07\u0E2A\u0E23\u0E49\u0E32\u0E07", rows: "3", maxLength: "8000" }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: runningBySession[session?.id] || !prompt.trim() || !session, children: runningBySession[session?.id] ? "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19" : "\u0E2A\u0E48\u0E07" })
213
+ ] })
214
+ ] })
215
+ ] }),
216
+ showSessions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionDrawer", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Chats" }),
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setShowSessions(false), children: "\xD7" })
220
+ ] }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-new", onClick: async () => {
222
+ await newSession();
223
+ setShowSessions(false);
224
+ }, children: "\uFF0B New chat" }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionRow", children: [
226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: async () => {
227
+ await choose(item.id);
228
+ setShowSessions(false);
229
+ }, children: item.title }),
230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionActions", children: [
231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => archive(item.id), children: "\u232B" }),
232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => remove(item.id), children: "\xD7" })
233
+ ] })
234
+ ] }, item.id)) })
235
+ ] }) }),
236
+ showSettings && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-modal", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "codex-chat-settings", children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Settings" }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setShowSettings(false), children: "\xD7" })
240
+ ] }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
242
+ "Provider",
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: settings.provider, onChange: (e) => {
244
+ const next = { ...settings, provider: e.target.value };
245
+ setSettings(next);
246
+ loadModels(next);
247
+ }, children: [
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "server", children: "OpenRouter key \u0E2B\u0E25\u0E31\u0E01\u0E02\u0E2D\u0E07\u0E23\u0E30\u0E1A\u0E1A" }),
249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "openrouter", children: "OpenRouter key \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "compatible", children: "OpenAI-compatible provider \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "google-ai-proxy", children: "Google AI (server key, Responses adapter)" })
252
+ ] })
253
+ ] }),
254
+ settings.provider !== "server" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
255
+ "API key",
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "password", value: settings.apiKey, placeholder: settings.provider === "google-ai-proxy" ? "Google AI API key (override server key)" : "API key \u0E08\u0E30\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E43\u0E19 global settings", onChange: (e) => setSettings({ ...settings, apiKey: e.target.value }) })
257
+ ] }),
258
+ settings.provider === "compatible" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
259
+ "Base URL",
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: settings.baseUrl, placeholder: "https://provider.example/v1", onChange: (e) => setSettings({ ...settings, baseUrl: e.target.value }) })
261
+ ] }),
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
263
+ "Model",
264
+ settings.provider === "compatible" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: settings.model, placeholder: "provider/model-id", onChange: (e) => setSettings({ ...settings, model: e.target.value }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { value: settings.model, onChange: (e) => setSettings({ ...settings, model: e.target.value }), children: models.length ? models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: model.id, children: [
265
+ model.name,
266
+ " \u2014 ",
267
+ model.id
268
+ ] }, model.id)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: settings.model, children: modelsState || settings.model }) }),
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "codex-chat-catalogStatus", children: modelsState })
270
+ ] }),
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Settings \u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14\u0E40\u0E1B\u0E47\u0E19 global \u0E41\u0E25\u0E30\u0E40\u0E01\u0E47\u0E1A\u0E43\u0E19 SQLite volume \u0E02\u0E2D\u0E07 service" }),
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-save", onClick: saveGlobalSettings, children: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01" })
273
+ ] }) })
274
+ ] });
275
+ }
276
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/CodexChatWidget.jsx"],
4
+ "sourcesContent": ["import { useEffect, useState } from 'react'\nimport './styles.css'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\n\nconst defaultSettings = { provider: 'server', model: 'cohere/north-mini-code:free', apiKey: '', baseUrl: '' }\n\nexport default function CodexChatWidget({\n apiBase = '',\n storageKey = 'codex-chat',\n initialSessionId,\n title = 'Codex Harness'\n}) {\n const apiBaseUrl = String(apiBase || '').replace(/\\/$/, '')\n const [sessions, setSessions] = useState([])\n const [session, setSession] = useState(null)\n const [prompt, setPrompt] = useState('')\n const [activityBySession, setActivityBySession] = useState({})\n const [runningBySession, setRunningBySession] = useState({})\n const [settings, setSettings] = useState(defaultSettings)\n const [models, setModels] = useState([])\n const [modelsState, setModelsState] = useState('')\n const [showSettings, setShowSettings] = useState(false)\n const [showSessions, setShowSessions] = useState(false)\n const [homeLabel, setHomeLabel] = useState('~/.pmai/workspace')\n\n async function refreshSessions(selectId) {\n let items = await (await fetch(apiBaseUrl + '/api/sessions')).json()\n if (!items.length) {\n const created = await (await fetch(apiBaseUrl + '/api/sessions', { method: 'POST' })).json()\n items = [created]\n }\n setSessions(items)\n const wanted = selectId || session?.id\n const id = items.some((item) => item.id === wanted) ? wanted : items[0].id\n const response = await fetch(apiBaseUrl + `/api/sessions?id=${id}`)\n if (response.ok) { setSession(await response.json()); localStorage.setItem(`${storageKey}-active-session`, id) }\n }\n useEffect(() => { const savedSession = localStorage.getItem(`${storageKey}-active-session`); refreshSessions(initialSessionId || savedSession || undefined); fetch(apiBaseUrl + '/api/settings').then((r) => r.json()).then(setSettings); fetch(apiBaseUrl + '/api/health').then((r) => r.json()).then((h) => { if (h.workspace) setHomeLabel('~/.pmai' + String(h.workspace).replace(/^\\/pmai/, '')) }).catch(() => {}) }, [])\n\n async function loadModels(nextSettings = settings) {\n setModelsState('\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 model list\u2026')\n const data = await (await fetch(apiBaseUrl + '/api/models', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(nextSettings), cache: 'no-store' })).json()\n setModels(data.models || []); setModelsState(data.error || data.message || data.label || `${(data.models || []).length} models`)\n }\n async function openSettings() { setShowSettings(true); await loadModels() }\n\n async function saveGlobalSettings() {\n const previous = `${settings.provider}/${settings.model}`\n const saved = await (await fetch(apiBaseUrl + '/api/settings', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify(settings) })).json()\n setSettings(saved)\n if (session && `${saved.provider}/${saved.model}` !== previous) setSession((current) => ({ ...current, messages: [...current.messages, { role: 'system', text: `Model changed: ${saved.model} (${saved.provider})` }] }))\n setShowSettings(false)\n }\n\n async function newSession() { const created = await (await fetch(apiBaseUrl + '/api/sessions', { method: 'POST' })).json(); await refreshSessions(created.id) }\n async function choose(id) { await refreshSessions(id) }\n async function archive(id) { await fetch(apiBaseUrl + '/api/sessions', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, archived: true }) }); await refreshSessions() }\n async function remove(id) { if (!confirm('Delete this chat permanently?')) return; await fetch(apiBaseUrl + `/api/sessions?id=${id}`, { method: 'DELETE' }); if (session?.id === id) setSession(null); await refreshSessions() }\n\n async function send(event) {\n event.preventDefault(); const text = prompt.trim(); const sessionId = session?.id\n if (!text || runningBySession[sessionId] || !sessionId) return\n setSession((x) => ({ ...x, messages: [...x.messages, { role: 'user', text }] }))\n setPrompt(''); setActivityBySession((x) => ({ ...x, [sessionId]: [{ text: '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E15\u0E23\u0E35\u0E22\u0E21\u0E07\u0E32\u0E19' }] })); setRunningBySession((x) => ({ ...x, [sessionId]: true }))\n try {\n const response = await fetch(apiBaseUrl + '/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ prompt: text, sessionId, settings }) })\n if (!response.ok || !response.body) { const error = await response.json(); throw new Error(error.error || `Request failed (${response.status})`) }\n const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let finalText = ''\n while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const frames = buffer.split('\\n\\n'); buffer = frames.pop() || ''\n for (const frame of frames) { const line = frame.split('\\n').find((x) => x.startsWith('data: ')); if (!line) continue; const data = JSON.parse(line.slice(6)); if (data.type === 'status') setActivityBySession((x) => ({ ...x, [sessionId]: [...(x[sessionId] || []), { text: data.text }] })); if (data.type === 'final') finalText = data.text; if (data.type === 'error') throw new Error(data.text) }\n }\n if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: 'assistant', text: finalText || '\u0E07\u0E32\u0E19\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E41\u0E25\u0E49\u0E27 \u0E41\u0E15\u0E48\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E2A\u0E23\u0E38\u0E1B' }] })); await refreshSessions(sessionId)\n } catch (error) { if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: 'assistant', text: `\u0E40\u0E01\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14: ${error.message}` }] })) }\n finally { setActivityBySession((x) => ({ ...x, [sessionId]: [] })); setRunningBySession((x) => ({ ...x, [sessionId]: false })) }\n }\n\n return <main className=\"codex-chat-app\">\n <aside className=\"codex-chat-sidebar\"><button className=\"codex-chat-new\" onClick={newSession}>\uFF0B New chat</button><div className=\"codex-chat-sessionList\">{sessions.map((item) => <div className=\"codex-chat-sessionRow\" key={item.id}><button className={item.id === session?.id ? 'session activeSession' : 'session'} onClick={() => choose(item.id)}>{item.title}<small>{new Date(item.updatedAt).toLocaleString()}</small></button><div className=\"codex-chat-sessionActions\"><button title=\"Archive\" onClick={() => archive(item.id)}>\u232B</button><button title=\"Delete\" onClick={() => remove(item.id)}>\u00D7</button></div></div>)}</div><button className=\"codex-chat-settingsBtn\" onClick={openSettings}>\u2699 Settings</button></aside>\n <section className=\"codex-chat-shell\"><header><div><b>{title}</b><span>{settings.model} \u00B7 {homeLabel}</span></div><div className=\"codex-chat-headerActions\"><button className=\"codex-chat-sessionsBtn\" onClick={() => setShowSessions(true)}>\u2630 Chats</button><button className=\"codex-chat-modelBtn\" onClick={openSettings}>\u2304 Model</button><span className=\"codex-chat-status\">\u25CF Ready</span></div></header><section className=\"codex-chat-messages\" aria-live=\"polite\">{!session ? <div className=\"codex-chat-welcome\">\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 session\u2026</div> : <>{session.messages.length === 0 && <div className=\"codex-chat-welcome\"><h1>\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E38\u0E22\u0E01\u0E31\u0E1A Codex</h1><p>\u0E40\u0E25\u0E37\u0E2D\u0E01 model \u0E2B\u0E23\u0E37\u0E2D provider \u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13\u0E44\u0E14\u0E49\u0E08\u0E32\u0E01 Settings</p></div>}{session.messages.map((message, index) => message.role === 'system' ? <div className=\"codex-chat-systemMessage\" key={index}>{message.text}</div> : <article className={message.role} key={index}><label>{message.role === 'user' ? '\u0E04\u0E38\u0E13' : '\u0E04\u0E33\u0E15\u0E2D\u0E1A'}</label>{message.role === 'assistant' ? <div className=\"codex-chat-markdown\"><ReactMarkdown remarkPlugins={[remarkGfm]}>{message.text}</ReactMarkdown></div> : <pre>{message.text}</pre>}</article>)}{runningBySession[session.id] && <article className=\"codex-chat-assistant progress\"><label>\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19</label><ol>{(activityBySession[session.id] || []).map((item, index) => <li key={index} className={index === (activityBySession[session.id] || []).length - 1 ? 'active' : ''}>{item.text}</li>)}</ol></article>}</>}</section><div className=\"codex-chat-composer\"><div className=\"codex-chat-modelFooter\">{settings.provider} \u00B7 {settings.model}</div><form onSubmit={send}><textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} placeholder=\"\u0E40\u0E0A\u0E48\u0E19 \u0E15\u0E23\u0E27\u0E08\u0E44\u0E1F\u0E25\u0E4C\u0E43\u0E19 workspace \u0E41\u0E25\u0E49\u0E27\u0E2A\u0E23\u0E38\u0E1B\u0E42\u0E04\u0E23\u0E07\u0E2A\u0E23\u0E49\u0E32\u0E07\" rows=\"3\" maxLength=\"8000\" /><button disabled={runningBySession[session?.id] || !prompt.trim() || !session}>{runningBySession[session?.id] ? '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19' : '\u0E2A\u0E48\u0E07'}</button></form></div></section>\n {showSessions && <div className=\"codex-chat-sessionDrawer\"><section><header><b>Chats</b><button onClick={() => setShowSessions(false)}>\u00D7</button></header><button className=\"codex-chat-new\" onClick={async () => { await newSession(); setShowSessions(false) }}>\uFF0B New chat</button><div className=\"codex-chat-sessionList\">{sessions.map((item) => <div className=\"codex-chat-sessionRow\" key={item.id}><button className={item.id === session?.id ? 'session activeSession' : 'session'} onClick={async () => { await choose(item.id); setShowSessions(false) }}>{item.title}</button><div className=\"codex-chat-sessionActions\"><button onClick={() => archive(item.id)}>\u232B</button><button onClick={() => remove(item.id)}>\u00D7</button></div></div>)}</div></section></div>}\n {showSettings && <div className=\"codex-chat-modal\"><section className=\"codex-chat-settings\"><header><b>Settings</b><button onClick={() => setShowSettings(false)}>\u00D7</button></header><label>Provider<select value={settings.provider} onChange={(e) => { const next = { ...settings, provider: e.target.value }; setSettings(next); loadModels(next) }}><option value=\"server\">OpenRouter key \u0E2B\u0E25\u0E31\u0E01\u0E02\u0E2D\u0E07\u0E23\u0E30\u0E1A\u0E1A</option><option value=\"openrouter\">OpenRouter key \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19</option><option value=\"compatible\">OpenAI-compatible provider \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19</option><option value=\"google-ai-proxy\">Google AI (server key, Responses adapter)</option></select></label>{settings.provider !== 'server' && <label>API key<input type=\"password\" value={settings.apiKey} placeholder={settings.provider === 'google-ai-proxy' ? 'Google AI API key (override server key)' : 'API key \u0E08\u0E30\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E43\u0E19 global settings'} onChange={(e) => setSettings({ ...settings, apiKey: e.target.value })}/></label>}{settings.provider === 'compatible' && <label>Base URL<input value={settings.baseUrl} placeholder=\"https://provider.example/v1\" onChange={(e) => setSettings({ ...settings, baseUrl: e.target.value })}/></label>}<label>Model{settings.provider === 'compatible' ? <input value={settings.model} placeholder=\"provider/model-id\" onChange={(e) => setSettings({ ...settings, model: e.target.value })}/> : <select value={settings.model} onChange={(e) => setSettings({ ...settings, model: e.target.value })}>{models.length ? models.map((model) => <option key={model.id} value={model.id}>{model.name} \u2014 {model.id}</option>) : <option value={settings.model}>{modelsState || settings.model}</option>}</select>}<small className=\"codex-chat-catalogStatus\">{modelsState}</small></label><p>Settings \u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14\u0E40\u0E1B\u0E47\u0E19 global \u0E41\u0E25\u0E30\u0E40\u0E01\u0E47\u0E1A\u0E43\u0E19 SQLite volume \u0E02\u0E2D\u0E07 service</p><button className=\"codex-chat-save\" onClick={saveGlobalSettings}>\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01</button></section></div>}\n </main>\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ReactMarkdown", "remarkGfm"]
7
+ }