mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
codex/cursor-agent-auth-diagnostics
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4d0ea247a7 |
fix(desktop): swallow setTypeOfService EINVAL crashes in dev main (#647) (#1298)
* fix(desktop): swallow harmless setTypeOfService EINVAL crashes in dev main The packaged Electron entry (apps/packaged/src/logging.ts) already filters the undici "setTypeOfService EINVAL" crash that issue #895 introduced for the prod build, but the dev / source-built desktop entry was missing the parallel guard. Result: switching settings tabs in a from-source desktop run could fire a fresh fetch, undici would try to set IP_TOS on the outbound socket, the kernel would refuse on certain macOS / VPN configurations, and the rejection bubbled to Electron's default handler as the "JavaScript error in the main process" dialog reported in issue #647. Add the same defensive filter to apps/desktop: - isHarmlessSocketOptionError matches only the canonical undici shape (syscall name AND EINVAL code). A contradicting code (EACCES, EPERM, etc) explicitly fails the match so real bugs don't get hidden. - The uncaughtException handler logs harmless cases at warn and returns silently. For anything else it removes itself from the listener list and re-throws via setImmediate, restoring Node's default crash path so Electron's native dialog renders exactly as it would without this filter. - unhandledRejection mirrors the same harmless / fall-through split. The filter is installed BEFORE app.whenReady so it is armed by the time the renderer fires its first fetch. The helper is duplicated rather than imported from apps/packaged because AGENTS.md forbids cross-app private-source imports. The file header calls out the parallel and notes that the two copies should stay in sync until the helper is promoted to a shared workspace package (follow-up); the contract is identical so a regression in one will surface in the other's test suite. Tests in apps/desktop/tests/main/uncaught-exception.test.ts mirror apps/packaged/tests/logging.test.ts: 8 cases pinning the matcher shape, 2 cases pinning the handler's harmless-log-warn vs fall-through-rethrow split. Validated: pnpm guard, pnpm --filter @open-design/desktop typecheck, pnpm --filter @open-design/desktop build, and pnpm --filter @open-design/desktop test (14 passed, 10 new). * fix(desktop,packaged): fail-fast on non-harmless unhandled rejections The previous unhandledRejection listeners logged non-harmless reasons and returned, which kept the main process alive after any rejected promise. A real bug, a failed IPC registration, or any unexpected async exception was reduced to a console line instead of surfacing through Node/Electron's default crash path the filter was meant to preserve. Both copies now route non-harmless rejections through a parallel factory (createDesktopUnhandledRejectionHandler / createFatalUnhandledRejectionHandler) that mirrors the uncaughtException policy: harmless setTypeOfService EINVAL shapes log at warn and return, anything else logs at error, removes the listener, and re-throws via setImmediate. Listener removal happens before the scheduled throw, so the rethrown reason lands in the uncaughtException path with no recursion. Tests cover the harmless branch, the detach + ordered rethrow, and non-Error / primitive rejection reasons (Promise.reject(42)) which must fall through. Desktop suite: 13/13, packaged suite: 16/16. Flagged on PR #1298 by Siri-Ray and the codex P2 review thread; the two file copies stay in lockstep per the AGENTS.md sync invariant. --------- Co-authored-by: Nagendhra <nagendhra405@gmail.com> |
||
|
|
43f7fc536a |
Add Langfuse telemetry relay (#1296)
* Add Langfuse telemetry relay * Configure telemetry worker custom domain * Add telemetry relay health check * Harden telemetry relay config |
||
|
|
587c783dc0 |
feat(web): add Finalize design package + Continue in CLI buttons (#451) (#974)
* feat(daemon): expose resolvedDir on GET /api/projects/:id (#451 prereq)
Native projects (no metadata.baseDir) live at <projects root>/<id>, where
projects root is daemon-side state. The web client cannot reconstruct an
absolute path on its own, and shell.openPath on a relative path is
undefined behavior. Without resolvedDir, the upcoming Continue in CLI
button (#451) would render permanently disabled for native projects.
Mirrors PR #832's pattern of exposing designMdPath in its response.
Computed via the existing resolveProjectDir(...) helper. No behavior
change to existing callers; they ignore the new field.
Adds ProjectDetailResponse contract type and a focused projects-routes
test covering imported-folder, native, and unknown-id paths.
* feat(web): add parseProvenance helper for DESIGN.md staleness checks
Pure helper that extracts Project ID, design system, current artifact,
transcript message count, and generated UTC timestamp from the
`## Provenance` section emitted by the daemon's finalize synthesis
prompt (apps/daemon/src/finalize-design.ts). Used by useDesignMdState
to derive the Continue in CLI button's stale/fresh state without an
additional daemon endpoint.
Handles missing section, "none" sentinels for design system /
artifact, and malformed timestamps without throwing. Tests cover all
four branches.
* feat(web): add buildClipboardPrompt template for Continue in CLI
Inline single-source-of-truth template per #451 spec §3.4. Names the
project, the working directory, and the DESIGN.md-first operating
contract for the receiving `claude` CLI session. Trailing TODO is
the blank task slot the issue body specifies — left empty so the user
fills it in before submitting.
Also lands the shared copyToClipboard helper (jsdom-safe canonical path
+ execCommand fallback) so the new button and any future caller share
one fallback path, mirroring the inline pattern in FileViewer.tsx.
Tests cover happy-path field rendering, "none"/"unknown" sentinels
when DESIGN.md fields are absent, and both clipboard branches.
* feat(web): add useProjectDetail + useDesignMdState hooks
useProjectDetail wraps GET /api/projects/:id, surfacing the resolvedDir
field and falling back to metadata.baseDir for older daemons that don't
include it. Continue in CLI needs an absolute working directory so the
desktop bridge can openPath it; the web client never reconstructs the
path itself.
useDesignMdState fetches the project's file list, downloads DESIGN.md
when present, parses the Provenance section, and computes a stale
verdict by comparing the recorded generatedAt against the max mtime of
non-DESIGN.md files and the max conversation updatedAt. Drives the
button's three-state UI (disabled / fresh / stale) without a
daemon-side endpoint.
Tests cover happy path, fallback, and both stale branches plus the
pure computeStale helper for the null-timestamp edge case.
* feat(web): add useFinalizeProject hook with cancel + error-code mapping
Wraps POST /api/projects/:id/finalize/anthropic for the Finalize design
package button. Three concerns:
1. Lifecycle: idle → pending → success | error. Double-clicking the
button aborts the prior in-flight request before starting a new
one so the daemon never sees stacked finalize calls per project.
2. Cancellation: AbortController plumbed through fetch + a 130 s
timer (daemon timeout 120 s + 10 s buffer). Cancel returns to idle
cleanly — it's a user gesture, not an error surface.
3. Daemon error mapping: when the response is non-OK, body.error.code
drives the canonical user-facing toast string (table covers all
7 codes the daemon emits today plus a network-error catch-all).
body.error.details, when a string, surfaces alongside the category
message so account-usage-cap responses (Anthropic 400 →
UPSTREAM_UNAVAILABLE) can show the upstream's own reason instead
of just the daemon's category label — committed to lefarcen on
#450 verification reply.
Tests cover request body shape, all 8 error codes via it.each, the
network-error path, the details-surfacing branch, the cancel ⇒ idle
flow, and the unknown-code → catch-all message branch.
* feat(web): add useTerminalLaunch with electron/web detection
Capability-detected wrapper around window.electronAPI.openPath. On
desktop the bridge forwards to shell.openPath, which opens the OS
file manager at the project working directory (per Electron's
contract for directory paths — it is NOT a terminal launcher;
spawning a terminal application is deferred per #451 Non-goals). On
browser builds the hook reports web-fallback so the caller renders
a manual-instruction toast naming the working directory.
Treats any non-empty string return from shell.openPath as ok: false
so platform-specific failures surface the manual fallback toast.
Behavior is exercised end-to-end by the upcoming
ContinueInCliButton tests.
* feat(desktop): expose shell.openPath via electronAPI bridge
Adds an openPath bridge method that the Continue in CLI button (#451)
uses to surface the project working directory in the OS file manager.
shell.openPath is part of Electron's contract and resolves to '' on
success / a non-empty error string on failure; the IPC handler
forwards the result so the renderer can decide between the success
toast and the manual fallback toast without a separate error channel.
Empty / non-string inputs short-circuit to a self-describing error
string so the renderer never needs to worry about undefined-input
crashes from the main process.
Web side: extracts Window.electronAPI into a single global declaration
at apps/web/src/types/electron.d.ts so future bridge methods land in
one place. Two pre-existing inline declare-global blocks
(NewProjectPanel.tsx, providers/registry.ts) are deleted in favor of
that single source of truth — the inline ones each carried a partial
shape of the bridge and were diverging from the desktop preload.
* feat(web): add FinalizeDesignButton, ContinueInCliButton, ProjectActionsToolbar
Project-level toolbar that hosts the two new actions from #451.
Mounted between AppChromeHeader and the chat/workspace split (wiring
lands in the next commit). Per-file actions (Export PDF/PPTX/ZIP,
Deploy) stay in the FileViewer share menu.
FinalizeDesignButton has three idle labels driven by DESIGN.md
existence + staleness, plus a pending state with a spinner and a
cancel link that maps to useFinalizeProject's AbortController. Error
toasts are owned by ProjectView so the button doesn't carry its own
toast surface.
ContinueInCliButton renders disabled with a Finalize-pointing
tooltip when DESIGN.md is missing (so the workflow is discoverable
rather than hidden), enabled when fresh, and enabled with a stale
chip otherwise. Chip text is the spec's canonical "Spec is stale —
regenerate?" — N-turns-ago is deferred per spec §4.6.
Toast.tsx is a tiny transient component that mirrors
PromptTemplatePreviewModal's state-based toast pattern; supports a
secondary details line so daemon error envelopes that carry an
upstream explanation (e.g. Anthropic account-usage cap) can surface
the real reason alongside the daemon's category label.
CSS appends one block to apps/web/src/index.css mirroring the
existing app-project-title token usage; no CSS modules in this
repo (verified by grep).
* test(web): cover ContinueInCliButton states + interaction wiring
Three rendered states (DESIGN.md missing → disabled with the
Finalize-pointing tooltip; DESIGN.md fresh → enabled, no chip;
DESIGN.md stale → enabled with the canonical "Spec is stale —
regenerate?" chip), plus three onClick branches (no-op when
disabled, fires once when fresh, fires once when stale).
Click-handler integration with clipboard / shell.openPath / toast
lives in ProjectView (the button is presentational and takes the
handler in via props), so those are covered by Phase K's wiring +
the manual smoke test rather than the per-component test.
* feat(web): wire Continue in CLI + Finalize buttons into ProjectView
Mounts the new project-actions toolbar between AppChromeHeader and
the chat/workspace split, hidden when workspaceFocused so the
focus-mode artifact view stays uncluttered.
Wires the four hooks (useProjectDetail, useDesignMdState,
useFinalizeProject, useTerminalLaunch) to a single shared toast
surface. handleFinalize reads the request body from the existing
config: AppConfig prop and uses effectiveMaxTokens(config) to match
the chat-flow's maxTokens defaulting; on success it refreshes
useDesignMdState so the toolbar re-renders with the new chip state.
handleContinueInCli builds the literal clipboard prompt, copies it,
opens the working directory via shell.openPath on desktop /
falls through to a manual-instruction toast on browser, and surfaces
shell.openPath failures with a fallback toast that names the path.
Errors lift into the same toast surface (a useEffect tied to
finalize.error) so the daemon's category message + body.error.details
reach the user as the spec's two-line render — covered by hook test
16a in the prior commit.
⌘+Shift+K (mac) / Ctrl+Shift+K (others) is the keyboard
accelerator for Continue in CLI; capture-phase, platform-gated,
no-op when DESIGN.md is missing. Mirrors the existing FileWorkspace
shortcut idiom and does not collide with ⌘+P (Quick Switcher).
* fix(web): distinguish timeout abort from user cancel in useFinalizeProject
Addresses codex P2 finding on PR #974: the catch block treated every
AbortError as a user-initiated cancel and reset to idle silently. If
the internal 130 s timeout fired, users saw no failure signal but the
daemon's synthesis call may still have been in flight.
Adds a timedOutRef set inside the setTimeout callback before
controller.abort(), and branches in the catch: timeout → status
'error' with new TIMEOUT code ("Finalize timed out after 130 s. The
daemon may still be running."), user cancel → existing idle reset.
Reset the ref at the start of every trigger() so a previous timeout
doesn't poison the next call.
Adds one test using vi.useFakeTimers() that advances past 130_001 ms
and asserts the TIMEOUT error surface.
* fix(web): surface clipboard failures by rendering the prompt in the toast
Addresses codex P2 finding on PR #974: handleContinueInCli ignored
copyToClipboard's return value, so when both clipboard paths failed
(restricted browser context / insecure origin) the toast still said
"paste the prompt" though nothing had been copied — leaving users
with no manual-copy recourse in exactly the environments where the
fallback should help.
handleContinueInCli now branches on copyToClipboard's boolean return.
On failure the toast renders the prepared prompt in a scrollable
<pre> block and pins itself open (no auto-dismiss) so the user has
time to select-and-copy manually. Includes a Dismiss button + the
working directory in the secondary details line so the user has the
information needed to proceed.
The folder-open call is skipped on copy failure because there's
nothing to paste yet; the user copies first, then re-clicks Continue
in CLI when they're ready.
Toast component grows an optional Updating VS Code Server to version 41dd792b5e652393e7787322889ed5fdc58bd75b
Removing previous installation...
Installing VS Code Server for Linux x64 (41dd792b5e652393e7787322889ed5fdc58bd75b)
Downloading: 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 0% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 1% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 3% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 4% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 5% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 6% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 7% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 8% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 9% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 10% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 11% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 12% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 13% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 14% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 15% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 16% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 17% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 18% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 19% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 20% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 21% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 22% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 23% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 24% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 25% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 26% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 27% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 28% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 29% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 30% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 31% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 32% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 33% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 34% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 35% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 36% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 37% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 38% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 39% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 40% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 41% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 42% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 43% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 44% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 45% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 46% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 47% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 48% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 49% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 50% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 51% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 52% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 53% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 54% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 55% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 56% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 57% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 58% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 59% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 60% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 61% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 62% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 63% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 64% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 65% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 66% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 67% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 68% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 69% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 70% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 71% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 72% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 73% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 74% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 75% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 76% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 77% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 78% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 79% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 80% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 81% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 82% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 83% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 84% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 85% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 86% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 87% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 88% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 89% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 90% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 91% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 92% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 93% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 94% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 95% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 96% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 97% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 98% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99% 99%100%100%
Unpacking: 0% 1% 2% 3% 4% 5% 6% 7% 8% 9% 10% 11% 12% 13% 14% 15% 16% 17% 18% 19% 20% 21% 22% 23% 24% 25% 26% 27% 28% 29% 30% 31% 32% 33% 34% 35% 36% 37% 38% 39% 40% 41% 42% 43% 44% 45% 46% 47% 48% 49% 50% 51% 52% 53% 54% 55% 56% 57% 58% 59% 60% 61% 62% 63% 64% 65% 66% 67% 68% 69% 70% 71% 72% 73% 74% 75% 76% 77% 78% 79% 80% 81% 82% 83% 84% 85% 86% 87% 88% 89% 90% 91% 92% 93% 94% 95% 96% 97% 98% 99%100%
Unpacked 4009 files and folders to /home/bryan/.vscode-server/bin/41dd792b5e652393e7787322889ed5fdc58bd75b.
Looking for compatibility check script at /home/bryan/.vscode-server/bin/41dd792b5e652393e7787322889ed5fdc58bd75b/bin/helpers/check-requirements.sh
Running compatibility check script
Compatibility check successful (0) prop and the auto-dismiss
TTL is suppressed whenever code is present. CSS adds .od-toast-code
(monospace, max-height 240 with overflow-auto) and .od-toast-dismiss
styling.
Six new Toast tests cover details rendering, code rendering,
no-auto-dismiss when code is present, auto-dismiss when code is
absent, and the Dismiss button affordance.
* fix(web): make ContinueInCliButton disabled-state guidance visible
Addresses mrcfps's PR #974 review: native <button disabled> does
not fire hover/focus events in browsers we ship against, so a
`title` tooltip on the disabled button never surfaces. The only
guidance for the missing-DESIGN.md state was effectively invisible —
defeating the spec's "discoverable, not hidden" intent.
Renders the help text as a visible sibling <span> next to the
disabled button instead. Adds aria-describedby pointing the button
at the hint's id so assistive tech announces the explanation when
the disabled button gets focus. The native `disabled` attribute
stays so the button still can't be clicked or submitted.
CSS adds .project-actions-disabled-hint (muted italic, 11.5px,
matches the existing meta/secondary text style on this surface).
Test asserts the role="note" hint is in the DOM with the canonical
text and that the button's aria-describedby links to its id.
* fix(web): keep ProjectActionsToolbar at natural height inside the .app grid
The .app container was `grid-template-rows: auto 1fr` — only two
rows. Adding ProjectActionsToolbar as a third child between
AppChromeHeader and the chat/workspace split made the toolbar the
2nd grid item, so it took the `1fr` row (filling roughly half the
viewport) while the split got pushed into an implicit auto row at
its content's natural height. Surfaced as a screenshot from Bryan
showing the toolbar's background bleeding across most of the screen.
Extend grid-template-rows to `auto auto 1fr` and pin the split to
`grid-row: 3` explicitly. Now:
- Toolbar visible: row 1 = header (auto), row 2 = toolbar (auto),
row 3 = split (1fr, fills remaining viewport).
- Toolbar hidden via hidden=workspaceFocused → ProjectActionsToolbar
returns null, row 2 collapses to 0px (auto with no content), split
still fills row 3.
No JS changes; existing 609 tests still green.
* fix(web): guard useFinalizeProject state writes against superseded triggers
Addresses mrcfps's PR #974 P1 review on useFinalizeProject.ts:132
(also called out as P1.3 in lefarcen's deep-dive review).
Calling trigger() twice in quick succession aborted the first
controller and swapped abortRef to the new one, but the first
request's later AbortError catch still unconditionally called
setStatus('idle') / setError(null). That cleared the spinner and
re-enabled both toolbar buttons while the replacement finalize was
still pending — defeating the de-duplication this hook was meant to
enforce.
Adds an isCurrent() closure (`abortRef.current === controller`)
and gates every state-write site after the await: success path,
non-OK envelope path, AbortError-timeout, AbortError-cancel, and
network-error all bail early when the trigger has been superseded.
Per mrcfps: "make every state write request-scoped."
Regression test triggers twice in quick succession with a
never-resolving fetch, awaits the first promise (it rejects with
AbortError), and asserts status stays 'pending' rather than
collapsing to 'idle' under the replacement's lifetime.
* fix(desktop): allowlist-validate shell.openPath against registered project roots
Addresses mrcfps's PR #974 P1 review on runtime.ts:305 (also called
out as P1.2 in lefarcen's deep-dive review): the new
`shell:open-path` IPC handler accepted any renderer-supplied
string and forwarded it straight into Electron's `shell.openPath`,
widening the renderer→main trust boundary so XSS or a compromised
renderer dependency could open arbitrary local paths to the user.
Adds an explicit gate around the bridge:
1. validateExistingDirectory(p) — floor check that rejects empty
strings, relative paths, files, apps, and non-existent paths;
realpath-resolves so symlink games can't be used to register
one path and reach another.
2. createProjectRootGate() — Set-backed allowlist of
daemon-validated project working directories. The renderer
calls registerProjectRoot(absDir) once per project mount via
a new IPC method (preload bridge); the main process only
opens paths that pass both the floor check and the allowlist.
ProjectView wires the registration via a useEffect tied to
projectDetail.resolvedDir, so the active project's daemon-supplied
working directory is always the one being approved (not a renderer-
synthesized string).
Threat-model caveat documented in the runtime.ts comment block: an
attacker that fully controls the renderer can also call register
with arbitrary paths. Closing that gap fully requires a daemon-side
round-trip to derive the canonical resolvedDir from the daemon's
project registry, which is deferred to keep this PR focused.
Today's allowlist still defends against accidental misuse, bugs,
and common XSS payloads that don't know to call register first.
Adds apps/packaged/tests/desktop-project-root-gate.test.ts with 13
cases: floor-validation rejection cases (empty / relative / missing
/ file), happy-path resolution, symlink realpath canonicalization,
and the allowlist's register/isApproved/reset semantics. Mirrors
the existing apps/packaged/tests/desktop-url-allowlist.test.ts
pattern from PR #911 — the packaged workspace hosts the test
because apps/desktop has no vitest setup yet.
* fix(daemon): wire request-lifecycle abort signal through finalize route
Addresses mrcfps's PR #974 P1 review on
apps/daemon/src/server.ts:3831-3837 (also called out as P1.1 in
lefarcen's deep-dive review): `POST /api/projects/:id/finalize/anthropic`
called `finalizeDesignPackage(...)` without threading any
request-lifecycle abort, so cancelling the browser fetch only
aborted the UI-side request — the daemon's 60–120 s Anthropic call
kept running and still wrote DESIGN.md after the UI returned to idle.
Adds an AbortController inside the route handler, fired from
`res.on('close')`, and threads its signal into the existing
`signal?: AbortSignal` parameter on `FinalizeOptions`
(finalize-design.ts:70). `callAnthropicWithRetry` already passes
the signal through to the underlying fetch, so a client disconnect
now propagates all the way to the Anthropic SDK call.
Listener-event choice: `res.on('close')` is the canonical event
for "client disconnected before response was sent" in Express. The
common alternative `req.on('close')` fires whenever the *request*
stream finishes — for POST routes that means as soon as the
body-parser middleware drains the body, well before the route does
any work. Using req.on('close') would have flipped the abort
controller in every successful run; the test caught this empirically.
Caveat documented in the route's comment block: an abort fired
*after* the upstream response has been received but *before* the
atomic write completes still allows the write to land. The SDK
contract bounds the network round-trip, not the post-network disk
handoff.
Adds tests/finalize-route-abort.test.ts: spins up the test server,
mocks global fetch to capture the daemon-side AbortSignal at the
Anthropic call, sends the request via raw http (so we can destroy
the underlying socket), waits until the server reaches the
Anthropic call, then destroys the socket and asserts that the
daemon-side signal received an abort event within 5 s.
Three pre-existing project-watchers chokidar tests show flaky
timeouts under full-suite concurrency but pass in isolation;
unrelated to this fix.
* fix(daemon): refactor finalize-route-abort test to satisfy strict TS narrowing
The CI typecheck (`pnpm --filter @open-design/daemon typecheck`,
which runs both tsconfig.json and tsconfig.tests.json) caught what
my pre-push validation missed: TS narrowed `capturedSignal` to
literal `null` because vitest's mockImplementation closure can't
prove its callback runs, leaving the bare `let capturedSignal:
AbortSignal | null = null` permanently typed at its initial value.
At line 184 (`expect(capturedSignal?.aborted).toBe(true)`) the
right-hand side of the optional-chain became unreachable, and TS
flagged it as `Property 'aborted' does not exist on type 'never'`.
Switches to the standard ref-object pattern
(`const capture: { signal: AbortSignal | null } = { signal: null }`).
TS narrows let bindings inside closures conservatively but treats
object-property writes as opaque, so `capture.signal` reads
correctly across the closure boundary. Logic is unchanged.
(Pre-push oversight: ran `pnpm --filter @open-design/web typecheck`
but not the full repo `pnpm typecheck` after the daemon test
landed; the daemon's own typecheck would have caught this. Adding
`pnpm typecheck` back into the standard pre-push checklist.)
* fix(desktop): make shell.openPath gate daemon-controlled and reject .app bundles
Addresses lefarcen + mrcfps PR #974 P1 reviews on the previous path
allowlist (commit
|
||
|
|
223d35f073 | fix: improve Orbit and packaged data-dir startup errors (#1067) | ||
|
|
362b92c1a6 |
fix(packaged): swallow harmless setTypeOfService EINVAL from undici (#895) (#906)
* fix(packaged): swallow harmless setTypeOfService EINVAL from undici (#895) Opening Settings → Pets → Community in the packaged desktop app surfaced a native "JavaScript error in main process" dialog with `Uncaught Exception: Error: setTypeOfService EINVAL`. Root cause: undici's socket setup tries to set the IP_TOS byte for QoS / DSCP marking on outbound sockets, and the macOS kernel refuses with EINVAL on certain configurations (VPNs, IPv6-only sockets, some firewall postures). The byte is purely advisory — the socket itself is healthy and serves traffic — so the rejection should not crash the app. Two cooperating layers: 1. **`protocol.ts`** registers the `od://` scheme that backs every renderer page load and API call in the packaged build by forwarding through Node's global `fetch` (which is undici under the hood). Pulled the inner request handler out as `handleOdRequest()` so a test can drive it with a stub fetch, and wrapped the `await fetch()` in a try/catch that returns a 502 Response on failure. Without this, every undici rejection — not just `setTypeOfService` — propagated to Electron's default uncaught-exception path. Now the renderer sees a normal error response and the main process keeps running. 2. **`logging.ts`** adds a defensive `process.on('uncaughtException')` handler with a narrow filter, `isHarmlessSocketOptionError`, that only matches the canonical undici shape (message contains `setTypeOfService` AND code is `EINVAL` or message contains `EINVAL`). For any unrecognised error the handler re-throws via `setImmediate` so Node's default crash + Electron's crash dialog still fire end-to-end — a future regression that broadens the filter to "every EINVAL" is caught by the unit tests below. Tests: 13 new tests across `tests/protocol.test.ts` (5) and `tests/logging.test.ts` (8) pin both layers — including the explicit #895 regression case (fetch rejecting with the canonical EINVAL shape returns a 502 instead of throwing) and the negative guard against the filter swallowing real bugs (a generic write EINVAL or a setTypeOfService EACCES is *not* matched). Verified locally: - `pnpm --filter @open-design/packaged vitest tests/protocol.test.ts tests/logging.test.ts` → 13/13 - packaged `tsconfig.json` and `tsconfig.tests.json` (the CI killer): both clean - the one pre-existing failure in `tests/sidecars.test.ts` (`adds custom VP_HOME/bin …`) is independent of this PR — confirmed by stashing the change and re-running * fix(packaged): break recursive rethrow + tighten EINVAL filter (#906 review) @mrcfps and @lefarcen both flagged a real P1 in the first iteration: the non-harmless branch of the new uncaughtException handler rethrew via setImmediate while the same listener was still registered, so a real bug would re-enter the handler indefinitely instead of terminating. mrcfps reproduced the loop with a minimal Node script. lefarcen also flagged that the filter trusted the message string over a contradicting structured `code`. Both fixes: 1. **Recursive rethrow (P1).** Extract the handler as a named factory, `createFatalUncaughtExceptionHandler(logger)`, that captures itself in closure. On non-harmless errors the handler now `process.removeListener('uncaughtException', self)` before scheduling the rethrow. With no listener registered, the next throw lands in Node's default crash path — which is exactly what we wanted ("preserve fail-fast for real bugs"). 2. **`code` is authoritative (P2).** When `code` is present on the error, only `code === 'EINVAL'` qualifies. A contradicting `EACCES`/`EPERM` paired with `setTypeOfService EINVAL` in the message now slips through to the crash path instead of being swallowed. Message-based detection only fires when `code` is genuinely absent (some libuv builds don't populate it on raw thrown Errors). 3 new tests pin both fixes: - `does NOT match when code contradicts the message` and the EPERM variant guard against the P2 regression. - `removes itself from uncaughtException listeners before scheduling the rethrow` uses `vi.spyOn(process, 'removeListener')` and a stubbed setImmediate to assert the call order: removeListener fires before setImmediate schedules the throw. - `does NOT re-enter itself when invoked twice` is a belt-and-suspenders loop guard — even if a future refactor dropped the removeListener call, the test would catch runaway scheduling. Verified locally: - packaged vitest: 18/18 (was 13, +3 new tests; +2 negative-guard tests for the P2 filter; -0 deletions) - packaged tsc -p tsconfig.json --noEmit: clean - packaged tsc -p tsconfig.tests.json --noEmit (the CI killer): clean |
||
|
|
c0c1f6555c |
add support for VP_HOME environment variable in agent resolution (#859)
* feat: add support for VP_HOME environment variable in agent resolution - Introduced a new .node-version file to specify Node.js version. - Enhanced agent resolution tests to include scenarios for VP_HOME, ensuring proper handling of Vite+ global installs. - Updated platform code to resolve user-scoped home directories, allowing for custom Vite+ installations to be prioritized. - Added tests to verify that the resolution logic correctly honors the VP_HOME environment variable and integrates with existing user toolchain paths. * feat: enhance VP_HOME support in sidecars and platform - Updated the PACKAGED_CHILD_ENV_ALLOWLIST to include VP_HOME for environment variable forwarding. - Exported functions resolvePackagedChildBaseEnv and resolvePackagedPathEnv for better accessibility in tests. - Added tests to validate VP_HOME handling in packaged child environments and ensure correct path resolution. - Adjusted wellKnownUserToolchainBins to prioritize VP_HOME/bin in the toolchain path resolution. |
||
|
|
cb92c93ae0 |
Migrate beta release publishing to R2 (#805)
* Prebundle standalone web packaged runtime * Harden mac standalone prebundle policy * Prebundle mac daemon packaged runtime * Prune mac Electron locales * Maximize mac release artifact compression * Publish beta mac artifacts to R2 * Use remote R2 uploads for beta releases * Fail fast on beta R2 access issues * Use S3-compatible uploads for beta R2 releases * Decouple beta versioning from GitHub releases * Remove legacy beta metadata source * Address release beta review notes |
||
|
|
25a3ffd298 |
fix(daemon): add legacy data dir migrator
Add a one-shot OD_LEGACY_DATA_DIR migrator so packaged Desktop users can recover 0.3.x repo .od data into the 0.4.x data root. The migrator stages payloads before promotion, refuses unsafe merges and symlinks, rolls back failed promotion or marker writes, and extends packaged daemon startup handling for long migrations while failing fast on daemon exits. Closes #710 |
||
|
|
4368b8f163 |
feat(linux): add headless mode for install/start/stop operations (#686)
* feat(linux): add headless mode for install/start/stop operations * docs(linux): document headless mode commands and usage * refactor(linux-headless): write web-root.json instead of polling IPC for URL * fix(linux-headless): fail start when web identity never appears instead of returning success * docs(linux-headless): add use-case context and clarify launcher path dependency * fix(linux-headless): ensure launcher, identity and shutdown align with tools-pack - Bake OD_DATA_DIR into launcher so manual runs use the same paths as tools-pack - Validate web-root.json fields before accepting to reject stale identity - Remove web-root.json on successful stop - Add IPC server for graceful STATUS/SHUTDOWN handling * fix(linux-headless): create IPC server before writing web-root.json |
||
|
|
2455c70d51 |
fix(daemon, packaged): unbreak GUI-launched agent detection on minimal PATHs (#442) (#614)
* fix(daemon, packaged): unbreak GUI-launched agent detection on minimal PATHs (#442) GUI-launched daemons (Finder/Dock on macOS, .desktop on Linux) inherit a stripped PATH from launchd / the desktop session and don't read the user's interactive shell rc files, so any CLI installed via `npm i -g` under a sudo-free prefix like ~/.npm-global was silently undetected. Two layers maintained their own copies of the user-toolchain bin list (`apps/daemon/src/agents.ts:userToolchainDirs` for the resolver, `apps/packaged/src/sidecars.ts:resolvePackagedPathEnv` for the packaged sidecar PATH builder) and had already drifted on `~/.asdf/shims` and `~/Library/pnpm`. Adding ~/.npm-global to one side would have preserved the same anti-pattern. Extracts `wellKnownUserToolchainBins` into @open-design/platform as the single source of truth, has both layers consume it, and extends the list to cover ~/.npm-global/bin, ~/.npm-packages/bin, plus $NPM_CONFIG_PREFIX/bin / $npm_config_prefix/bin for users with a non-standard prefix. New vitest coverage in the platform package and a regression test in apps/daemon/tests/agents.test.ts modelled on the existing mise case. Verified end-to-end: under PATH=/usr/bin:/bin:/usr/sbin:/sbin (the launchd default a `.app` actually inherits), `resolveAgentExecutable` now returns ~/.npm-global/bin/gemini instead of null. * fix(daemon): isolate OD_AGENT_HOME resolution from $NPM_CONFIG_PREFIX leakage Address review feedback on PR #614: - mrcfps spotted that the daemon wrapper called wellKnownUserToolchainBins without passing `env`, so the helper read its default process.env. A developer or CI runner with NPM_CONFIG_PREFIX / npm_config_prefix exported would inject that real <prefix>/bin into resolveOnPath() even while the OD_AGENT_HOME hook pointed home at a temp fixture, making agent-detection tests environment-dependent. Reproduced locally: with OD_AGENT_HOME=<tmp> + NPM_CONFIG_PREFIX=/Users/me/.npm-global, resolveAgentExecutable({ bin: 'codex' }) returned the real machine's binary instead of null. Wrapper now passes `env: {}` whenever homeOverride is set, alongside the existing includeSystemBins gate. - lefarcen suggested also handling whitespace-only NPM_CONFIG_PREFIX values (e.g. NPM_CONFIG_PREFIX=" ") so the helper does not emit a bogus "<whitespace>/bin" entry. Added a .trim() check before appending. - lefarcen also suggested a comment pointer from the daemon wrapper to the platform helper so readers don't have to grep. Added the reference inline. Coverage: - packages/platform/tests/index.test.ts: new whitespace-prefix case. - apps/daemon/tests/agents.test.ts: new env-isolation regression asserting that OD_AGENT_HOME + NPM_CONFIG_PREFIX cannot leak the real prefix bin into the sandbox. * test(daemon): preserve $NPM_CONFIG_PREFIX across the env-isolation case (#614) Address mrcfps's second-round review on PR #614: the env-isolation regression sets `process.env.NPM_CONFIG_PREFIX = realPrefix` in its body and then unconditionally `delete`s it in `finally`. On a developer machine or CI runner that already exported `NPM_CONFIG_PREFIX`, that mutates the worker-wide env for every later test, making downstream env-sensitive assertions order-dependent. Move the save/restore into the file's existing afterEach hook (mirroring the OD_AGENT_HOME / OD_DAEMON_URL / OD_TOOL_TOKEN pattern) and drop the in-test `delete`. Same coverage, no worker-state mutation. * fix(platform): prioritise $NPM_CONFIG_PREFIX over the conventional npm guesses (#614) Address mrcfps's third-round review on PR #614: when the user has explicitly configured a prefix via $NPM_CONFIG_PREFIX (or $npm_config_prefix), that's where `npm i -g` puts the *current* binaries. The conventional guesses ~/.npm-global / ~/.npm-packages often hold *stale* installs from an older prefix the user has since rewritten — searching the env-driven prefix first matches npm's own resolution order (env > .npmrc > default) and gives "explicit beats convention" semantics. Move the env-driven push above the conventional `dirs.push(.npm-global, .npm-packages)`. Add a vitest case in the platform package that asserts $NPM_CONFIG_PREFIX/bin's index in the result is strictly less than ~/.npm-global/bin's and ~/.npm-packages/bin's. `resolveOnPath()` and the packaged PATH builder both preserve insertion order, so first hit wins and the new ordering propagates to both layers. * fix(platform): lift $NPM_CONFIG_PREFIX above every conventional bin (#614) Address mrcfps's fourth-round review on PR #614: the previous fix only moved $NPM_CONFIG_PREFIX/bin ahead of ~/.npm-global / ~/.npm-packages, but ~/.local/bin still appeared earlier in the array. Under a minimal GUI-launch PATH a stale agent in ~/.local/bin (also a shared dumping ground for pip --user / cargo install / hand-built binaries) could outrank the user's *current* explicit npm prefix. Move the env-driven push to the head of `dirs` so the explicit prefix wins over every conventional location below — ~/.local/bin included. Matches npm's own resolution order (env > .npmrc > default) across the whole list, not just the npm-prefix block. Tightened the existing order test to assert `explicitIdx === 0` and that ~/.local/bin's index is strictly greater than the explicit prefix's index, so a future drift would fail loudly. |
||
|
|
3935aeb421 |
Optimize packaged mac artifact size (#424)
* optimize mac package payload reporting * optimize(pack): package standalone web runtime * optimize(pack): default to standalone web runtime * chore(release): bump beta base version * fix(pack): compress mac artifacts and report packaged version * fix(pack): preserve Next server fallback * fix(pack): clarify standalone startup failures * fix(release): gate beta platform builds * fix(web): bind standalone backend to parent * fix(pack): harden standalone and beta publishing |
||
|
|
e5ed4bbc76 |
fix(packaged): preserve *_API_KEY env vars for CLI agents (#404)
* Forward API key env vars to packaged sidecars ## Summary Forward provider credential environment variables from the packaged desktop app process to packaged sidecar children. ## Why The packaged app currently forwards only a small fixed environment allowlist to sidecars: - HOME - LANG - LC_ALL - LOGNAME - TMPDIR - USER This prevents CLI agents launched by the packaged daemon from seeing provider credentials such as `CODEX_API_KEY`, even when the desktop app itself was launched with that variable set. ## Fix This keeps the existing allowlist and additionally forwards environment variables ending in: - `_API_KEY` - `_TOKEN` I intentionally did not change PATH or Bun-related behavior in this PR, since CLI discovery already works. The issue here is specifically credential forwarding. ## Verification I verified the root cause locally by patching the packaged output: `@open-design/packaged/dist/index.mjs` After forwarding `CODEX_API_KEY`, the packaged desktop app was able to launch Codex successfully. ## Testing I did not run the full project build locally. I verified the behavior by patching the installed macOS app output and confirming that forwarding `CODEX_API_KEY` fixes the Codex launch failure. * Add includeProviderSecrets parameter to env functions Updated. Provider-secret forwarding is now daemon-only: *_API_KEY / *_TOKEN are included only when spawning the daemon sidecar, while the web/Next sidecar stays on the original minimal allowlist. |
||
|
|
e3b5bf7fbc |
fix(packaged): include nvm/fnm/mise agent CLI bins in packaged PATH (#364)
* fix(packaged): include nvm/fnm/mise agent CLI bins in packaged PATH `resolvePackagedPathEnv` constructs the daemon PATH for packaged builds (DMG/AppImage/NSIS), but it only includes hardcoded locations (.local/bin, homebrew, etc.) and misses npm-global agent CLIs installed under a version manager. Add `collectNvmFnmBins()` to @open-design/platform that dynamically discovers nvm (`~/.nvm/versions/node/*/bin`), fnm, and mise npm-global bin directories. Wire it into `resolvePackagedPathEnv` alongside the existing static candidates. This complements the dev-mode fix in #346 (agents.ts: userToolchainDirs) by covering the packaged launcher path as well. * fix(packaged): move nvm/fnm bin discovery into packaged-runtime sidecars Keep @open-design/platform focused on generic OS/process primitives by moving the version-manager PATH discovery helpers (existingDirsUnder, collectNvmFnmBins) into apps/packaged/src/sidecars.ts where they are actually consumed by resolvePackagedPathEnv. |
||
|
|
a40d817d28 |
Add mac packaged runtime and beta release flow (#170)
* feat(pack): add mac packaged runtime control plane * feat(pack): harden mac packaged runtime lifecycle Keep packaged state namespace-scoped, make daemon paths explicit through sidecar launch env, and add conservative desktop identity/logging fallbacks for local mac package validation. * feat(pack): add mac beta release flow * fix(pack): generate mac update feed fallback * fix(pack): write portable beta checksums * fix(pack): make beta artifacts portable * fix(pack): clean up mac install visuals * fix(pack): address packaged runtime review feedback |