Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ file is the human-readable summary of what changed and why.
desktop reads its sessions over the sandbox's own authenticated API and
uploads them like any others — filed under the project folder the sandbox
belongs to, alongside that project's local sessions. A sandbox that is off
simply uploads on its next start; nothing is lost in between.
simply uploads on its next start; nothing is lost in between. An **Upload
Session Logs** menu item (under "Get a new API Key..." in the CoDev menu on
macOS, the File menu on Windows/Linux) runs the same sweep on demand — for
when you want a session analyzed now rather than at the next launch.
([#313](https://github.com/quickbeard/codev-code/pull/313))
- **A dropped model connection retries instead of silently stopping the agent** —
when the connection to the model died mid-response (corporate proxies are fond
Expand Down
2 changes: 1 addition & 1 deletion DIVERGENCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ from the squash message first, or use a plain merge.
| `package.json` (root) | `name` `opencode` → `codev-code`; `repository.url` → `github.com/quickbeard/codev-code`; added `release` script delegating to packages/opencode; added `bump` script pointing at the fork-only `script/bump-version.ts`; removed the `sso`, `dev:stats`, and `random` scripts; added the `lint-staged` devDependency and a `lint-staged` config block (`"*": "prettier --write --ignore-unknown"`) driving the fork-only `.husky/pre-commit` | The root manifest carries the fork's name (nothing reads it — root is private and unpublished; `packages/script` reads only `packageManager` — so this is cosmetic and also removes the name collision with the `packages/opencode` workspace member, whose name MUST stay `opencode`: build.ts derives bun compile targets and dist dir names from it). `bun run release` and `bun run bump` work from the repo root. `sso` logs into upstream's AWS org (`--sso-session=opencode`) and `dev:stats` runs inside an sst shell against upstream's production stage — neither works without anomalyco credentials; `random` is inert cruft (`echo 'Random script'`). `lint-staged` keeps staged files at Prettier's fixed point on commit, so the formatting drift PR #261 had to clean up cannot silently reaccumulate; `--ignore-unknown` skips files Prettier has no parser for (`bun.lock`, `.husky/*`, binaries) and `.prettierignore` still exempts `DIVERGENCES.md` and the docs merge base. | Keep our name + release/bump lines and the removals (drop any new upstream-infra-only scripts too); take upstream's other fields. |
| `packages/opencode/package.json` | bin key `opencode` → `codev` (shim path `./bin/opencode` unchanged); added `release` script; added dependencies `"@codev/brand": "workspace:*"`, `"@codev/gateway": "workspace:*"` (the latter for the gateway-key migration's shared-auth-file read, see the config.ts migration row), and `"@codev/upload": "workspace:*"` (the session uploader — see the session-upload wiring row) | Installed command is `codev` — matches the CLI's `scriptName` and help text (the hub's command is `codevhub`; it relinquished the `codev` bin in codev-ai ≥ 0.4.0). `bun run release` publishes the version in this file (upstream-synced; script/release.ts is fork-only). | Keep our bin key, release line, and dependency lines; take upstream's other fields including `version`. |
| Session-upload wiring: `packages/opencode/src/cli/cmd/tui.ts`, `packages/opencode/src/cli/cmd/run.ts`, `packages/opencode/src/cli/cmd/debug/index.ts` | tui.ts + run.ts: `spawnUploadDaemon()` (from the fork-only `@codev/upload` package) fire-and-forgotten at handler start — spawns a detached, unref'd, stdio-ignored `codev debug upload` child. debug/index.ts: registered the fork-only `UploadCommand` (debug/upload.ts, new file), the entry that child runs. | Standalone parity with the hub's launch-time upload daemon: every agent launch ships prior codev sessions to the analysis backend in the background. All gating (opt-out env `CODEV_DISABLE_AUTO_UPLOAD`, signed-in check, the hub-shared lockfile) lives inside `@codev/upload` — the wiring is one guarded call per entry point that can never block or fail the launch. | Keep the import + `spawnUploadDaemon()` line in both handlers and the debug registration; take upstream's surrounding changes. |
| Desktop session upload: `packages/desktop/src/main/sidecar.ts`, `packages/desktop/src/main/server.ts`, `packages/desktop/src/main/index.ts`, `packages/desktop/package.json` | sidecar.ts: after `Server.listen` succeeds, fire `runStartupUpload()` (from the fork-only `@codev/upload`, statically imported so electron-vite bundles it), and handle a `{type: "upload", servers?}` sidecar command that re-runs it — runs are chained on an in-process promise (the hub-shared lockfile makes a concurrent run skip, not queue, so an unchained sandbox run posted right after startup's local run would silently drop its work sessions). server.ts: `SidecarListener` gains `upload(servers?)` (posts that command with structured-clone-safe `ServerSource` records; no-op once exited). index.ts: `sandboxUploadSources()` maps every `ready` sandbox to a source (`runtime.url`/`username`/`password` + `config.projectDir`, the HOST dir, so a project's local and sandboxed sessions land in one analysis bucket); a `sandboxes.subscribe` diff fires `server?.upload(sources)` whenever a sandbox newly reaches ready (its whole history becomes readable at that moment), with a post-`initialize` catch-up for sandboxes ready before the sidecar listened; the gateway-sso `onConfigured` hook also calls `server?.upload(sandboxUploadSources())` so a fresh sign-in ships the backlog (work sessions included) immediately, and startup shows an OS `Notification` ("Session upload is paused") when `@codev/upload/nudge`'s `shouldNudgeSignIn()` reports the stall — the desktop counterpart of the TUI toast, every launch until sign-in. package.json: `"@codev/upload": "workspace:*"` under devDependencies — deliberately, like `@codev/gateway`: electron-vite externalizes `dependencies` into the (unshipped) runtime node_modules, and only devDependency workspace packages get bundled. | The desktop runs the server in-process under Electron's Node with no `codev` CLI in the bundle, so the CLI's detached `codev debug upload` spawn cannot cover it; running the uploader inside the sidecar utility process (never Electron main, which is UI-latency sensitive) reuses the whole pipeline — same store (the sidecar only overrides `XDG_STATE_HOME`), same shared auth file, same hub-shared lockfile, so desktop, CLI, and hub can never upload concurrently. The `#sqlite` bun/node conditional import inside `@codev/upload` (same pattern as the server core's `#db`) is what lets the identical package run on `bun:sqlite` in the CLI binary and `node:sqlite` in the sidecar. | Keep the sidecar `upload` command + startup trigger, the listener's `upload()`, the `onConfigured` call + startup Notification, and the devDependency placement; take upstream's surrounding changes. |
| Desktop session upload: `packages/desktop/src/main/sidecar.ts`, `packages/desktop/src/main/server.ts`, `packages/desktop/src/main/index.ts`, `packages/desktop/package.json` | sidecar.ts: after `Server.listen` succeeds, fire `runStartupUpload()` (from the fork-only `@codev/upload`, statically imported so electron-vite bundles it), and handle a `{type: "upload", servers?}` sidecar command that re-runs it — runs are chained on an in-process promise (the hub-shared lockfile makes a concurrent run skip, not queue, so an unchained sandbox run posted right after startup's local run would silently drop its work sessions). server.ts: `SidecarListener` gains `upload(servers?)` (posts that command with structured-clone-safe `ServerSource` records; no-op once exited). index.ts: `sandboxUploadSources()` maps every `ready` sandbox to a source (`runtime.url`/`username`/`password` + `config.projectDir`, the HOST dir, so a project's local and sandboxed sessions land in one analysis bucket); a `sandboxes.subscribe` diff fires `server?.upload(sources)` whenever a sandbox newly reaches ready (its whole history becomes readable at that moment), with a post-`initialize` catch-up for sandboxes ready before the sidecar listened; the gateway-sso `onConfigured` hook also calls `server?.upload(sandboxUploadSources())` so a fresh sign-in ships the backlog (work sessions included) immediately; an "Upload Session Logs" menu item (desktop-menu.ts: the fork-only `upload.sessions` `DesktopMenuAction`, placed under "Get a new API Key..." in the macOS app menu and the Windows/Linux File menu; threaded through desktop-menu-actions.ts, menu.ts and ipc.ts's handler deps, generic renderer dispatch already covers it) runs the same sweep on demand with an "Uploading session logs" Notification as feedback; and startup shows an OS `Notification` ("Session upload is paused") when `@codev/upload/nudge`'s `shouldNudgeSignIn()` reports the stall — the desktop counterpart of the TUI toast, every launch until sign-in. package.json: `"@codev/upload": "workspace:*"` under devDependencies — deliberately, like `@codev/gateway`: electron-vite externalizes `dependencies` into the (unshipped) runtime node_modules, and only devDependency workspace packages get bundled. | The desktop runs the server in-process under Electron's Node with no `codev` CLI in the bundle, so the CLI's detached `codev debug upload` spawn cannot cover it; running the uploader inside the sidecar utility process (never Electron main, which is UI-latency sensitive) reuses the whole pipeline — same store (the sidecar only overrides `XDG_STATE_HOME`), same shared auth file, same hub-shared lockfile, so desktop, CLI, and hub can never upload concurrently. The `#sqlite` bun/node conditional import inside `@codev/upload` (same pattern as the server core's `#db`) is what lets the identical package run on `bun:sqlite` in the CLI binary and `node:sqlite` in the sidecar. | Keep the sidecar `upload` command + startup trigger, the listener's `upload()`, the `onConfigured` call + startup Notification, and the devDependency placement; take upstream's surrounding changes. |
| `bun.lock` | Mirrors the bin key rename above, the fork-only workspace packages/deps (`@codev/gateway`, `@codev/brand`, `@codev/upload`), and the fork-only `lint-staged` devDependency | Lockfile records workspace bin mappings and workspace deps. Known quirk: the root workspace stanza still says `"name": "opencode"` — bun never syncs the root manifest's name into an existing lock (verified: neither `bun install` nor `--lockfile-only` rewrites it); it self-corrects only on a from-scratch regeneration and is harmless meanwhile. Don't hand-edit it. | Regenerate with `bun install`; never hand-merge. |
| `.prettierignore` | Appended `DIVERGENCES.md` and `packages/web/src/content/docs/` (each with a comment saying why) | Prettier realigns every row of this file's changed-files table, so a one-row edit churns all ~120 rows — noise in the one file that records upstream-conflict guidance, and a guaranteed conflict on the next sync. Nothing in the repo formatted it before (the pre-push hook only runs `bun typecheck`), so this pins the status quo rather than changing it. The docs directory is the upstream merge base (see `packages/web/FORK-DOCS.md`): nothing builds it, its whole job is to surface upstream doc diffs during a sync, and formatting it only adds conflict surface — plus Prettier's MDX printer mangles `index.mdx`, collapsing the fenced code blocks inside `<TabItem>` into inline text. | Keep our lines; take upstream's other entries. |
| `packages/tui/package.json` | Added dependencies `"@codev/gateway": "workspace:*"`, `"@codev/brand": "workspace:*"`, and `"@codev/upload": "workspace:*"` (the latter's `/nudge` + `/spawn` subpaths only — sqlite-free modules for the upload sign-in toast and the post-configure daemon respawn) | The fork's gateway configure dialog imports SSO/backend/config-block logic from the fork-only `packages/codev-gateway`; the "Open docs" command takes its URL from `DOCS_URL` in the fork-only `packages/codev-brand` instead of hardcoding the host. | Keep our dependency lines; take upstream's other fields. |
Expand Down
9 changes: 9 additions & 0 deletions packages/app/src/desktop-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type DesktopMenuPlatform = "macos" | "windows"
export type DesktopMenuAction =
| "app.checkForUpdates"
| "app.relaunch"
| "upload.sessions"
| "edit.undo"
| "edit.redo"
| "edit.cut"
Expand Down Expand Up @@ -82,6 +83,7 @@ export const DESKTOP_MENU: DesktopMenu[] = [
{ type: "item", label: "Check for Updates...", action: "app.checkForUpdates", enabled: "updater" },
{ type: "item", label: "Settings", command: "settings.open", accelerator: { macos: "Cmd+," } },
{ type: "item", label: "Get a new API Key...", command: "gateway.apiKey.new" },
{ type: "item", label: "Upload Session Logs", action: "upload.sessions" },
{ type: "item", label: "Reload Webview", action: "view.reload" },
{ type: "item", label: "Restart", action: "app.relaunch" },
{ type: "item", label: "Export Logs...", command: "logs.export" },
Expand Down Expand Up @@ -119,6 +121,13 @@ export const DESKTOP_MENU: DesktopMenu[] = [
command: "gateway.apiKey.new",
platforms: ["windows"],
},
// macOS carries this in the app menu below its API-key sibling.
{
type: "item",
label: "Upload Session Logs",
action: "upload.sessions",
platforms: ["windows"],
},
{
type: "item",
label: "New Window",
Expand Down
4 changes: 4 additions & 0 deletions packages/desktop/src/main/desktop-menu-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createMainWindow, updateTitlebar } from "./windows"
export type DesktopMenuActionHandlers = Partial<{
checkForUpdates: () => void
relaunch: () => void
uploadSessions: () => void
}>

export function runDesktopMenuAction(
Expand All @@ -19,6 +20,9 @@ export function runDesktopMenuAction(
case "app.relaunch":
handlers.relaunch?.()
return
case "upload.sessions":
handlers.uploadSessions?.()
return
case "window.new":
createMainWindow()
return
Expand Down
14 changes: 14 additions & 0 deletions packages/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ const main = Effect.gen(function* () {
readySandboxes = ready
if (newlyReady) server?.upload(sandboxUploadSources())
})
// Fork: the "Upload Session Logs" menu item — a user-triggered sweep of
// local and work sessions through the sidecar's uploader (same gates:
// opt-out env, sign-in, shared lock). The notification is the only
// feedback a background run can give from here; the sidecar posts no
// completion message back.
const uploadSessionLogs = () => {
server?.upload(sandboxUploadSources())
new Notification({
title: "Uploading session logs",
body: "New and changed sessions are being uploaded for analysis in the background.",
}).show()
}
const stopSidecars = async () => {
await killSidecar()
wslServers.stopAll()
Expand Down Expand Up @@ -330,6 +342,7 @@ const main = Effect.gen(function* () {
registerIpcHandlers({
killSidecar: () => killSidecar(),
relaunch,
uploadSessions: uploadSessionLogs,
awaitInitialization: Effect.fnUntraced(
function* () {
logger.log("awaiting server ready")
Expand Down Expand Up @@ -519,6 +532,7 @@ const main = Effect.gen(function* () {
checkForUpdates: () => {
void showUpdaterDialog(updater, true)
},
uploadSessions: uploadSessionLogs,
relaunch: () => {
relaunch()
},
Expand Down
2 changes: 2 additions & 0 deletions packages/desktop/src/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Deps = {
resolveAppPath: (appName: string) => Promise<string | null>
updater: UpdaterController
showUpdater: () => Promise<void> | void
uploadSessions: () => void
setBackgroundColor: (color: string) => void
exportDebugLogs: () => Promise<string>
recordFatalRendererError: (error: FatalRendererError) => Promise<void> | void
Expand Down Expand Up @@ -301,6 +302,7 @@ export function registerIpcHandlers(deps: Deps) {
runDesktopMenuAction(BrowserWindow.fromWebContents(event.sender), action, {
checkForUpdates: () => void deps.showUpdater(),
relaunch: deps.relaunch,
uploadSessions: deps.uploadSessions,
})
})
}
Expand Down
2 changes: 2 additions & 0 deletions packages/desktop/src/main/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Deps = {
trigger: (id: string) => void
checkForUpdates: () => void
relaunch: () => void
uploadSessions: () => void
}

export function createMenu(deps: Deps) {
Expand Down Expand Up @@ -52,6 +53,7 @@ function nativeItem(entry: DesktopMenuEntry, deps: Deps): MenuItemConstructorOpt
runDesktopMenuAction(BrowserWindow.getFocusedWindow(), action, {
checkForUpdates: deps.checkForUpdates,
relaunch: deps.relaunch,
uploadSessions: deps.uploadSessions,
})
}
if (entry.href) {
Expand Down
Loading