xedoc-cli 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.
Files changed (53) hide show
  1. package/README.md +111 -0
  2. package/bin/xedoc.mjs +233 -0
  3. package/build/client/assets/api.accounts-CHoT6sYP.js +0 -0
  4. package/build/client/assets/api.accounts._accountId-BixDYyx8.js +0 -0
  5. package/build/client/assets/api.accounts._accountId.authenticate-CLzgv3py.js +0 -0
  6. package/build/client/assets/api.accounts._accountId.authenticate.callback-Bqhz8sDl.js +0 -0
  7. package/build/client/assets/api.accounts._accountId.models-VIVpVZrj.js +0 -0
  8. package/build/client/assets/api.accounts._accountId.rate-limits-C9iUvNFt.js +0 -0
  9. package/build/client/assets/api.accounts._accountId.runtime-settings-6XNKTx--.js +0 -0
  10. package/build/client/assets/api.accounts.export-romutOT6.js +0 -0
  11. package/build/client/assets/api.accounts.import-BiWq2jPz.js +0 -0
  12. package/build/client/assets/api.auth.exchange-CkPWDiGV.js +0 -0
  13. package/build/client/assets/api.auth.session-DOeXMy_2.js +0 -0
  14. package/build/client/assets/api.auth.status-FGYJfLVM.js +0 -0
  15. package/build/client/assets/api.chats-C6-gVaXw.js +0 -0
  16. package/build/client/assets/api.chats._chatId-DaXHa-ln.js +0 -0
  17. package/build/client/assets/api.chats._chatId.context-DlBgjpCD.js +0 -0
  18. package/build/client/assets/api.chats._chatId.files-DVZKcoNV.js +0 -0
  19. package/build/client/assets/api.chats._chatId.git._operation-8Xtyz4hF.js +0 -0
  20. package/build/client/assets/api.chats._chatId.interrupt-B71lrxkc.js +0 -0
  21. package/build/client/assets/api.chats._chatId.messages-iVzJcgvw.js +0 -0
  22. package/build/client/assets/api.chats._chatId.server-requests._requestId.respond-BLhOUK8A.js +0 -0
  23. package/build/client/assets/api.users-BLvMDkJE.js +0 -0
  24. package/build/client/assets/api.workspaces.directories-B0tllRDq.js +0 -0
  25. package/build/client/assets/app-layout-C1QtSnIO.js +1 -0
  26. package/build/client/assets/app-shell-Bcxy4tS4.js +1 -0
  27. package/build/client/assets/app-shell-DdKuH37F.css +1 -0
  28. package/build/client/assets/chat-DrelwLpW.js +1 -0
  29. package/build/client/assets/connect-0oYrfAJT.js +1 -0
  30. package/build/client/assets/document-title-Dn4sU16M.js +1 -0
  31. package/build/client/assets/entry.client-CM3vH2bv.js +1 -0
  32. package/build/client/assets/favicon.ico-BGeA4faG.js +0 -0
  33. package/build/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  34. package/build/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  35. package/build/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  36. package/build/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  37. package/build/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  38. package/build/client/assets/health-OjIoaxn7.js +0 -0
  39. package/build/client/assets/home-DSWMwIrE.js +1 -0
  40. package/build/client/assets/jsx-runtime-B9QlDcuB.js +1 -0
  41. package/build/client/assets/label-FUU4pCWu.js +1 -0
  42. package/build/client/assets/manifest-9479dd15.js +1 -0
  43. package/build/client/assets/react-dom-KYDDPtOx.js +1 -0
  44. package/build/client/assets/root-Bh_1OXF0.css +2 -0
  45. package/build/client/assets/root-DKEnPIjJ.js +1 -0
  46. package/build/client/assets/session-provider-FKGj36EG.js +1 -0
  47. package/build/client/favicon.svg +1 -0
  48. package/build/client/icons.svg +24 -0
  49. package/build/server/index.js +1 -0
  50. package/package.json +88 -0
  51. package/prisma/schema.prisma +171 -0
  52. package/server/index.mjs +261 -0
  53. package/server/sqlite-setup.mjs +155 -0
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # xedoc
2
+
3
+ xedoc is a single React Router Framework Mode app, built with Vite, for managing Codex accounts, chats, chat execution, workspace browsing, and live assistant output.
4
+
5
+ ## What It Does
6
+
7
+ - Stores users, Codex accounts, chat metadata, runs, and live message projections in a local SQLite database through Prisma.
8
+ - Starts Codex account authentication through the local `codex app-server` JSON-RPC flow.
9
+ - Isolates each Codex account with a separate `CODEX_HOME`.
10
+ - Stores a working directory on each chat so different chats can target different local projects.
11
+ - Executes chat prompts against the selected Codex account and reads settled transcripts back from Codex runtime/session data.
12
+ - Serves the ChatGPT-style web UI and `/api/*` resource routes from one same-origin app.
13
+ - Streams live chat updates through authenticated Socket.IO rooms on `/socket.io`.
14
+
15
+ ## Setup
16
+
17
+ The easiest local install is the npm CLI:
18
+
19
+ ```bash
20
+ npx xedoc
21
+ ```
22
+
23
+ By default the CLI creates a standalone SQLite database at
24
+ `~/.xedoc/xedoc.db`, prepares the schema, and serves the app at
25
+ `http://127.0.0.1:6354`. Open the app in a browser and set the server password
26
+ on first visit. Use a different database file with:
27
+
28
+ ```bash
29
+ npx xedoc --database-path ~/.local/share/xedoc.db
30
+ ```
31
+
32
+ Common CLI options:
33
+
34
+ - `--port <port>` changes the web server port.
35
+ - `--workspace-root <path>` changes the directory tree visible to the app.
36
+ - `--accounts-home <path>` changes where Codex account state is stored.
37
+ - `--database-path <path>` changes where the SQLite database is stored.
38
+ - `--skip-setup` skips SQLite schema setup.
39
+
40
+ For repository development:
41
+
42
+ ```bash
43
+ pnpm install
44
+ cp .env.example .env
45
+ pnpm prisma:generate
46
+ pnpm db:setup
47
+ pnpm dev
48
+ ```
49
+
50
+ On first visit, the web app asks you to set the server password and stores a
51
+ hashed password plus token signing secret in the SQLite database. Later browser
52
+ sessions exchange that password for a bearer token, then store the token,
53
+ browser user id, and active account id in local storage.
54
+
55
+ The default `DATABASE_URL` uses a SQLite file under `.xedoc/` in the
56
+ project. Set `DATABASE_URL=file:/absolute/path/to/xedoc.db` to use a
57
+ specific database file.
58
+
59
+ ## Scripts
60
+
61
+ - `pnpm dev` starts the React Router dev server.
62
+ - `pnpm build` builds the app.
63
+ - `pnpm start` serves the production React Router build through `server/index.mjs` and attaches Socket.IO.
64
+ - `pnpm db:setup` creates or updates the local SQLite schema.
65
+ - `pnpm prisma:generate` regenerates Prisma Client.
66
+ - `pnpm run publish` publishes the package to npm with public access. npm runs `prepack`, which builds the production bundle first.
67
+ - `pnpm run publish:dry-run` checks the npm package contents without publishing.
68
+
69
+ ## npm Releases
70
+
71
+ Package releases are published by GitHub Actions when a tag matching `v*` is
72
+ pushed. The workflow uses npm trusted publishing, so configure the package on
73
+ npmjs.com with this GitHub repository and the workflow file
74
+ `.github/workflows/npm-publish.yml` before pushing the first release tag.
75
+
76
+ ## Codex Account Isolation
77
+
78
+ Each Codex account runs as its own local `codex app-server` process. The server sets `CODEX_HOME` per account so auth files, config, sessions, cache, and other Codex state stay under:
79
+
80
+ ```text
81
+ ~/.xedoc/accounts/<accountId>
82
+ ```
83
+
84
+ Set `CODEX_ACCOUNTS_HOME` to change the base directory. This isolates Codex account state only; Codex still runs as the same host user and can access whatever that user can access.
85
+
86
+ Set `CODEX_WORKSPACE_ROOT` to the directory the web app can browse for chat
87
+ working directories. Local development defaults to the current user's home
88
+ directory, for example `/home/ubuntu`.
89
+
90
+ ## API Entry Points
91
+
92
+ - `GET /health`
93
+ - `GET /api/auth/status`
94
+ - `POST /api/auth/exchange`
95
+ - `GET /api/auth/session`
96
+ - `POST /api/users`
97
+ - `GET /api/accounts`
98
+ - `POST /api/accounts`
99
+ - `GET /api/accounts/:accountId`
100
+ - `PATCH /api/accounts/:accountId`
101
+ - `DELETE /api/accounts/:accountId`
102
+ - `POST /api/accounts/:accountId/authenticate`
103
+ - `POST /api/accounts/:accountId/authenticate/callback`
104
+ - `GET /api/chats`
105
+ - `POST /api/chats`
106
+ - `GET /api/chats/:chatId`
107
+ - `PATCH /api/chats/:chatId`
108
+ - `DELETE /api/chats/:chatId`
109
+ - `GET /api/chats/:chatId/messages`
110
+ - `POST /api/chats/:chatId/messages`
111
+ - `GET /api/workspaces/directories`
package/bin/xedoc.mjs ADDED
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process"
3
+ import { mkdir, readFile } from "node:fs/promises"
4
+ import { homedir } from "node:os"
5
+ import { dirname, join, resolve } from "node:path"
6
+ import { fileURLToPath } from "node:url"
7
+ import { createRequire } from "node:module"
8
+
9
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..")
10
+ const require = createRequire(import.meta.url)
11
+ const packageJson = JSON.parse(
12
+ await readFile(join(packageRoot, "package.json"), "utf8"),
13
+ )
14
+
15
+ const options = parseArgs(process.argv.slice(2))
16
+ if (options.help) {
17
+ printHelp()
18
+ process.exit(0)
19
+ }
20
+ if (options.version) {
21
+ console.log(packageJson.version)
22
+ process.exit(0)
23
+ }
24
+
25
+ const appHome = resolveHomePath(
26
+ options.home ?? process.env.XEDOC_HOME ?? "~/.xedoc",
27
+ )
28
+ await mkdir(appHome, { recursive: true, mode: 0o700 })
29
+
30
+ const port = options.port ?? process.env.PORT ?? "6354"
31
+ const host = options.host ?? process.env.HOST ?? "127.0.0.1"
32
+ const accountsHome = resolveHomePath(
33
+ options.accountsHome ??
34
+ process.env.CODEX_ACCOUNTS_HOME ??
35
+ join(appHome, "accounts"),
36
+ )
37
+ const workspaceRoot = resolveHomePath(
38
+ options.workspaceRoot ?? process.env.CODEX_WORKSPACE_ROOT ?? homedir(),
39
+ )
40
+ const databasePath = resolveHomePath(
41
+ options.databasePath ??
42
+ process.env.XEDOC_DATABASE ??
43
+ join(appHome, "xedoc.db"),
44
+ )
45
+ await mkdir(dirname(databasePath), { recursive: true, mode: 0o700 })
46
+ const databaseUrl =
47
+ options.databaseUrl ??
48
+ process.env.DATABASE_URL ??
49
+ `file:${databasePath}`
50
+
51
+ const codexBin = require.resolve("@openai/codex/bin/codex.js")
52
+ const env = {
53
+ ...process.env,
54
+ CODEX_ACCOUNTS_HOME: accountsHome,
55
+ CODEX_ARGS: options.codexArgs ?? process.env.CODEX_ARGS ?? `${codexBin} app-server`,
56
+ CODEX_COMMAND: options.codexCommand ?? process.env.CODEX_COMMAND ?? process.execPath,
57
+ CODEX_WORKSPACE_ROOT: workspaceRoot,
58
+ DATABASE_URL: databaseUrl,
59
+ HOST: host,
60
+ NODE_ENV: "production",
61
+ PORT: port,
62
+ }
63
+
64
+ await mkdir(accountsHome, { recursive: true, mode: 0o700 })
65
+ if (!options.skipPrismaGenerate) {
66
+ await runPrisma(["generate"], env)
67
+ }
68
+ if (!options.skipSetup) {
69
+ await setupSqliteDatabase(env)
70
+ }
71
+
72
+ const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`
73
+ console.log(`xedoc: ${url}`)
74
+ console.log("Set the server password in your browser on first visit.")
75
+ console.log(`Workspace root: ${workspaceRoot}`)
76
+ console.log("Press Ctrl+C to stop.")
77
+
78
+ await runServer(env)
79
+
80
+ function parseArgs(argv) {
81
+ const parsed = {}
82
+ for (let index = 0; index < argv.length; index += 1) {
83
+ const arg = argv[index]
84
+ if (arg === "--help" || arg === "-h") {
85
+ parsed.help = true
86
+ } else if (arg === "--version" || arg === "-v") {
87
+ parsed.version = true
88
+ } else if (arg === "--skip-setup") {
89
+ parsed.skipSetup = true
90
+ } else if (arg === "--skip-prisma-generate") {
91
+ parsed.skipPrismaGenerate = true
92
+ } else if (arg.startsWith("--")) {
93
+ const [name, inlineValue] = arg.split("=", 2)
94
+ const value = inlineValue ?? argv[++index]
95
+ if (!value || value.startsWith("--")) {
96
+ fail(`${name} requires a value.`)
97
+ }
98
+ assignOption(parsed, name, value)
99
+ } else {
100
+ fail(`Unknown argument: ${arg}`)
101
+ }
102
+ }
103
+ return parsed
104
+ }
105
+
106
+ function assignOption(parsed, name, value) {
107
+ switch (name) {
108
+ case "--accounts-home":
109
+ parsed.accountsHome = value
110
+ return
111
+ case "--codex-args":
112
+ parsed.codexArgs = value
113
+ return
114
+ case "--codex-command":
115
+ parsed.codexCommand = value
116
+ return
117
+ case "--database-url":
118
+ parsed.databaseUrl = value
119
+ return
120
+ case "--database-path":
121
+ parsed.databasePath = value
122
+ return
123
+ case "--home":
124
+ parsed.home = value
125
+ return
126
+ case "--host":
127
+ parsed.host = value
128
+ return
129
+ case "--port":
130
+ parsed.port = value
131
+ return
132
+ case "--workspace-root":
133
+ parsed.workspaceRoot = value
134
+ return
135
+ default:
136
+ fail(`Unknown option: ${name}`)
137
+ }
138
+ }
139
+
140
+ async function runPrisma(args, env) {
141
+ await run(process.execPath, [
142
+ require.resolve("prisma/build/index.js"),
143
+ ...args,
144
+ "--schema",
145
+ join(packageRoot, "prisma/schema.prisma"),
146
+ ], {
147
+ cwd: packageRoot,
148
+ env,
149
+ stdio: "inherit",
150
+ })
151
+ }
152
+
153
+ async function runServer(env) {
154
+ await run(process.execPath, [join(packageRoot, "server/index.mjs")], {
155
+ cwd: packageRoot,
156
+ env,
157
+ stdio: "inherit",
158
+ })
159
+ }
160
+
161
+ async function setupSqliteDatabase(env) {
162
+ const previousDatabaseUrl = process.env.DATABASE_URL
163
+ process.env.DATABASE_URL = env.DATABASE_URL
164
+ try {
165
+ const { setupSqliteDatabase } = await import(
166
+ join(packageRoot, "server/sqlite-setup.mjs")
167
+ )
168
+ await setupSqliteDatabase()
169
+ } finally {
170
+ if (previousDatabaseUrl === undefined) {
171
+ delete process.env.DATABASE_URL
172
+ } else {
173
+ process.env.DATABASE_URL = previousDatabaseUrl
174
+ }
175
+ }
176
+ }
177
+
178
+ function run(command, args, options) {
179
+ return new Promise((resolveRun, rejectRun) => {
180
+ const child = spawn(command, args, options)
181
+ const forwardSigint = () => child.kill("SIGINT")
182
+ const forwardSigterm = () => child.kill("SIGTERM")
183
+ process.once("SIGINT", forwardSigint)
184
+ process.once("SIGTERM", forwardSigterm)
185
+ child.on("exit", (code, signal) => {
186
+ process.removeListener("SIGINT", forwardSigint)
187
+ process.removeListener("SIGTERM", forwardSigterm)
188
+ if (code === 0 || signal) {
189
+ resolveRun()
190
+ } else {
191
+ rejectRun(new Error(`${command} exited with code ${code}`))
192
+ }
193
+ })
194
+ child.on("error", rejectRun)
195
+ })
196
+ }
197
+
198
+ function resolveHomePath(path) {
199
+ if (path === "~") {
200
+ return homedir()
201
+ }
202
+ if (path.startsWith("~/")) {
203
+ return join(homedir(), path.slice(2))
204
+ }
205
+ return resolve(path)
206
+ }
207
+
208
+ function fail(message) {
209
+ console.error(message)
210
+ process.exit(1)
211
+ }
212
+
213
+ function printHelp() {
214
+ console.log(`xedoc ${packageJson.version}
215
+
216
+ Usage:
217
+ npx xedoc [options]
218
+
219
+ Options:
220
+ --port <port> Web server port. Defaults to 6354.
221
+ --host <host> Web server host. Defaults to 127.0.0.1.
222
+ --workspace-root <path> Directory tree visible in the app. Defaults to your home directory.
223
+ --accounts-home <path> Codex account state directory. Defaults to ~/.xedoc/accounts.
224
+ --database-path <path> SQLite database path. Defaults to ~/.xedoc/xedoc.db.
225
+ --database-url <url> SQLite DATABASE_URL. Defaults to file:<database-path>.
226
+ --skip-setup Do not create the SQLite database schema.
227
+ --codex-command <command> Codex command used for new accounts.
228
+ --codex-args <args> Codex command arguments used for new accounts.
229
+ --home <path> App data directory. Defaults to ~/.xedoc.
230
+ --help Show this help.
231
+ --version Print the package version.
232
+ `)
233
+ }
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ import{j as r,t as s}from"./jsx-runtime-B9QlDcuB.js";import{t}from"./app-shell-Bcxy4tS4.js";var a=s(),o=r(function(){return(0,a.jsx)(t,{})});export{o as default};