diff --git a/README.ar.md b/README.ar.md index 7cc71aefe..a0e4a08c2 100644 --- a/README.ar.md +++ b/README.ar.md @@ -800,7 +800,7 @@ Issues و PRs و skills جديدة وأنظمة تصميم جديدة، كلّه شكراً لكلّ من ساعد في دفع Open Design للأمام — بكود، بوثائق، بملاحظات، بـ skills جديدة، بأنظمة تصميم جديدة، أو حتى بـ issue حادّة. كلّ مساهمة حقيقية تهمّ، والجدار أدناه أسهل طريقة لقول ذلك علناً. - Open Design contributors + Open Design contributors إن شحنت أوّل PR — مرحباً. تصنيف [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) هو نقطة الدخول. @@ -817,9 +817,9 @@ Issues و PRs و skills جديدة وأنظمة تصميم جديدة، كلّه - - - Open Design star history + + + Open Design star history diff --git a/README.de.md b/README.de.md index 83f6aac0e..d34706ae7 100644 --- a/README.de.md +++ b/README.de.md @@ -726,7 +726,7 @@ Vollständiger Walkthrough, Merge-Messlatte, Code Style und was wir nicht annehm Danke an alle, die Open Design vorangebracht haben: durch Code, Docs, Feedback, neue Skills, neue Design Systems oder auch ein scharfes Issue. Jeder echte Beitrag zählt, und die Wand unten ist die einfachste Art, das laut zu sagen. - Open Design contributors + Open Design contributors Wenn Sie Ihren ersten PR gemergt haben: willkommen. Das Label [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) ist der Einstiegspunkt. @@ -743,9 +743,9 @@ Das SVG oben wird täglich von [`.github/workflows/metrics.yml`](.github/workflo - - - Open Design star history + + + Open Design star history diff --git a/README.es.md b/README.es.md index 36a526a3a..a5ef84401 100644 --- a/README.es.md +++ b/README.es.md @@ -787,7 +787,7 @@ Walkthrough completo, estándar de merge, code style y lo que no aceptamos → [ Gracias a todas las personas que han ayudado a mover Open Design hacia adelante: con código, docs, feedback, nuevas skills, nuevos design systems o incluso un issue preciso. Toda contribución real cuenta, y el muro de abajo es la forma más simple de decirlo en voz alta. - Contribuidores de Open Design + Contribuidores de Open Design Si ya enviaste tu primer PR, bienvenido. La etiqueta [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) es el punto de entrada. @@ -804,9 +804,9 @@ El SVG anterior se regenera diariamente mediante [`.github/workflows/metrics.yml - - - Historial de estrellas de Open Design + + + Historial de estrellas de Open Design diff --git a/README.fr.md b/README.fr.md index 24fad9eae..6da7b27e2 100644 --- a/README.fr.md +++ b/README.fr.md @@ -733,7 +733,7 @@ Guide complet, critères de merge, style de code et refus fréquents → [`CONTR Merci à toutes les personnes qui font avancer Open Design : code, docs, retours, nouveaux Skills, nouveaux Design Systems ou issues bien ciblées. Chaque vraie contribution compte. - Contributeurs Open Design + Contributeurs Open Design Si vous avez livré votre première PR, bienvenue. Le label [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) est le point d’entrée. @@ -750,9 +750,9 @@ Le SVG ci-dessus est régénéré chaque jour par [`.github/workflows/metrics.ym - - - Historique des stars Open Design + + + Historique des stars Open Design diff --git a/README.ja-JP.md b/README.ja-JP.md index 94bf9ada9..abc90bc39 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -723,7 +723,7 @@ Issue、PR、新 Skill、新 Design System を歓迎します。最も効果の コード、ドキュメント、フィードバック、新 Skill、新 Design System、あるいは鋭い Issue — あらゆる形で Open Design を前進させてくださったすべての方に感謝します。すべての実質的なコントリビューションは大切であり、以下のウォールは最もシンプルな感謝の表明です。 - Open Design コントリビューター + Open Design コントリビューター 初めての PR を送った方 — ようこそ。[`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) ラベルがエントリポイントです。 @@ -740,9 +740,9 @@ Issue、PR、新 Skill、新 Design System を歓迎します。最も効果の - - - Open Design star history + + + Open Design star history diff --git a/README.ko.md b/README.ko.md index 07575e56b..51f91743d 100644 --- a/README.ko.md +++ b/README.ko.md @@ -726,7 +726,7 @@ daemon 부팅 시 `PATH`에서 자동 감지됩니다. 설정 필요 없음. 스 Open Design을 앞으로 나아가게 도와준 모든 분께 감사드립니다 — 코드, 문서, 피드백, 새 skill, 새 디자인 시스템, 또는 날카로운 이슈 하나라도. 모든 진짜 기여가 의미 있고, 아래의 벽이 가장 직접적인 "감사합니다"입니다. - Open Design 컨트리뷰터 + Open Design 컨트리뷰터 첫 PR을 보냈다면 — 환영합니다. [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) 레이블이 시작점입니다. @@ -743,9 +743,9 @@ Open Design을 앞으로 나아가게 도와준 모든 분께 감사드립니다 - - - Open Design star history + + + Open Design star history diff --git a/README.md b/README.md index 8bc3cb1ed..58093bde0 100644 --- a/README.md +++ b/README.md @@ -1040,7 +1040,7 @@ Full walkthrough, bar-for-merging, code style, and what we don't accept → [`CO Thanks to everyone who has helped move Open Design forward — through code, docs, feedback, new skills, new design systems, or even a sharp issue. Every real contribution counts, and the wall below is the easiest way to say so out loud. - Open Design contributors + Open Design contributors If you've shipped your first PR — welcome. The [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) label is the entry point. @@ -1057,9 +1057,9 @@ The SVG above is regenerated daily by [`.github/workflows/metrics.yml`](.github/ - - - Open Design star history + + + Open Design star history diff --git a/README.pt-BR.md b/README.pt-BR.md index 680e5e389..a3e954615 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -730,7 +730,7 @@ Walkthrough completo, barra para mergear, estilo de código e o que não aceitam Obrigado a todas as pessoas que ajudaram a empurrar o Open Design pra frente — via código, docs, feedback, novas skills, novos design systems ou até uma issue afiada. Toda contribuição real conta, e a parede abaixo é a forma mais simples de dizer isso em voz alta. - Contribuidoras e contribuidores do Open Design + Contribuidoras e contribuidores do Open Design Se você acabou de mandar seu primeiro PR — bem-vindo. A label [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) é o ponto de entrada. @@ -747,9 +747,9 @@ O SVG acima é regenerado diariamente por [`.github/workflows/metrics.yml`](.git - - - Histórico de estrelas do Open Design + + + Histórico de estrelas do Open Design diff --git a/README.ru.md b/README.ru.md index 3d757c990..42955c1ab 100644 --- a/README.ru.md +++ b/README.ru.md @@ -729,7 +729,7 @@ Issues, PR, новые skills и новые design systems приветству Спасибо всем, кто помогает двигать Open Design вперёд — кодом, документацией, обратной связью, новыми skills, новыми design systems или просто точным issue. Вклад любого реального масштаба здесь важен, а стена ниже — самый простой способ сказать это вслух. - Contributors Open Design + Contributors Open Design Если вы только что отправили свой первый PR — добро пожаловать. Метка [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) — хорошая точка входа. @@ -746,9 +746,9 @@ SVG выше ежедневно пересобирается workflow [`.github/ - - - История звёзд Open Design + + + История звёзд Open Design diff --git a/README.tr.md b/README.tr.md index d733d087d..3958abe59 100644 --- a/README.tr.md +++ b/README.tr.md @@ -887,7 +887,7 @@ Tam walkthrough, merge çıtası, code style ve kabul etmediklerimiz → [`CONTR Open Design'ı kod, doküman, feedback, yeni skill, yeni design system veya keskin bir issue ile ileri taşıyan herkese teşekkürler. Her gerçek katkı önemlidir; aşağıdaki wall bunu yüksek sesle söylemenin en kolay yolu. - Open Design contributors + Open Design contributors İlk PR'ını gönderdiysen hoş geldin. [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) label'ı giriş noktasıdır. @@ -904,9 +904,9 @@ Yukarıdaki SVG [`.github/workflows/metrics.yml`](.github/workflows/metrics.yml) - - - Open Design star history + + + Open Design star history diff --git a/README.uk.md b/README.uk.md index b45e7eaa4..2cbbc9daa 100644 --- a/README.uk.md +++ b/README.uk.md @@ -729,7 +729,7 @@ OD не зупиняється на коді. Та сама поверхня ч Дякуємо всім, хто допоміг просувати Open Design — через код, документацію, зворотний зв'язок, нові навички, нові системи дизайну або навіть гостре питання. Кожен реальний внесок рахується, а стіна нижче — найпростіший спосіб сказати це вголос. - Контриб'ютори Open Design + Контриб'ютори Open Design Якщо ви злили свій перший PR — ласкаво просимо. Мітка [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) — це точка входу. @@ -746,9 +746,9 @@ SVG вище перегенерується щодня [`.github/workflows/metri - - - Історія зірок Open Design + + + Історія зірок Open Design diff --git a/README.zh-CN.md b/README.zh-CN.md index 4735ca902..a5dbd86ae 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -722,7 +722,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。流式分发逻辑在 [ 感谢每一位让 Open Design 变得更好的朋友 —— 无论是写代码、修文档、提 issue、加 skill 还是加 design system,每一次真实贡献都会被记住。下面这面墙是最直观的「Thank you」。 - Open Design 贡献者 + Open Design 贡献者 第一次提 PR?欢迎从 [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) 标签起步。 @@ -739,9 +739,9 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。流式分发逻辑在 [ - - - Open Design star history + + + Open Design star history diff --git a/README.zh-TW.md b/README.zh-TW.md index 8b7b2c612..092a54683 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -1006,7 +1006,7 @@ Daemon 啟動時從 `PATH` 自動檢測,無需配置。流式分發邏輯在 [ 感謝每一位讓 Open Design 變得更好的朋友 —— 無論是寫程式碼、修文檔、提 issue、加 skill 還是加 design system,每一次真實貢獻都會被記住。下面這面牆是最直觀的「Thank you」。 - Open Design 貢獻者 + Open Design 貢獻者 第一次提 PR?歡迎從 [`good-first-issue`/`help-wanted`](https://github.com/nexu-io/open-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22%2C%22help+wanted%22) 標籤起步。 @@ -1023,9 +1023,9 @@ Daemon 啟動時從 `PATH` 自動檢測,無需配置。流式分發邏輯在 [ - - - Open Design star history + + + Open Design star history diff --git a/apps/daemon/src/import-export-routes.ts b/apps/daemon/src/import-export-routes.ts index 15f9c6c90..5e615f570 100644 --- a/apps/daemon/src/import-export-routes.ts +++ b/apps/daemon/src/import-export-routes.ts @@ -6,6 +6,7 @@ import { inlineRelativeAssets, type InlineAssetReader, } from './inline-assets.js'; +import { isSandboxModeEnabled } from './sandbox-mode.js'; export interface RegisterImportRoutesDeps extends RouteDeps<'db' | 'http' | 'uploads' | 'node' | 'ids' | 'paths' | 'imports' | 'auth' | 'projectStore' | 'conversations' | 'projectFiles' | 'validation'> {} @@ -28,6 +29,11 @@ export function registerImportRoutes(app: Express, ctx: RegisterImportRoutesDeps const { insertConversation } = ctx.conversations; const { setTabs } = ctx.projectFiles; const { validateProjectDesignSystemId } = ctx.validation; + const rejectSandboxFolderImport = () => + isSandboxModeEnabled(process.env) + ? 'folder imports are disabled when OD_SANDBOX_MODE is enabled' + : null; + app.post( '/api/import/claude-design', importUpload.single('file'), @@ -107,6 +113,10 @@ export function registerImportRoutes(app: Express, ctx: RegisterImportRoutesDeps if (typeof baseDir !== 'string' || !baseDir.trim()) { return sendApiError(res, 400, 'BAD_REQUEST', 'baseDir required'); } + const sandboxReason = rejectSandboxFolderImport(); + if (sandboxReason) { + return sendApiError(res, 400, 'BAD_REQUEST', sandboxReason); + } let trustedPickerImport = false; if (isDesktopAuthGateActive()) { const secret = desktopAuthSecret(); @@ -204,6 +214,10 @@ export function registerImportRoutes(app: Express, ctx: RegisterImportRoutesDeps if (typeof baseDir !== 'string' || !baseDir.trim()) { return sendApiError(res, 400, 'BAD_REQUEST', 'baseDir required'); } + const sandboxReason = rejectSandboxFolderImport(); + if (sandboxReason) { + return sendApiError(res, 400, 'BAD_REQUEST', sandboxReason); + } let trustedPickerImport = false; if (isDesktopAuthGateActive()) { const secret = desktopAuthSecret(); diff --git a/apps/daemon/src/media-config.ts b/apps/daemon/src/media-config.ts index cb35381b2..466f77b99 100644 --- a/apps/daemon/src/media-config.ts +++ b/apps/daemon/src/media-config.ts @@ -41,6 +41,7 @@ import path from 'node:path'; import { MEDIA_PROVIDERS } from './media-models.js'; import { expandHomePrefix } from './home-expansion.js'; import { resolveXAIBearer } from './xai-credentials.js'; +import { isSandboxModeEnabled } from './sandbox-mode.js'; const PROVIDER_IDS = MEDIA_PROVIDERS.map((p) => p.id); type ProviderEntry = { apiKey?: string; baseUrl?: string; model?: string }; @@ -291,6 +292,7 @@ function apiKeyFromCodexAuth(data: unknown): string { } async function resolveOpenAIAuthFileCredential(): Promise { + if (isSandboxModeEnabled(process.env)) return null; const home = os.homedir(); const codexAuth = await readJsonIfPresent( path.join(home, '.codex', 'auth.json'), @@ -318,6 +320,8 @@ async function resolveXAIOAuthCredential( }; } + if (isSandboxModeEnabled(process.env)) return null; + // 2. Borrow the xAI OAuth token Hermes wrote to ~/.hermes/auth.json // when the user ran `hermes auth add xai-oauth`. A user who has already authorized // Hermes doesn't have to run a second OAuth dance inside OD. diff --git a/apps/daemon/src/project-root.ts b/apps/daemon/src/project-root.ts new file mode 100644 index 000000000..26693047e --- /dev/null +++ b/apps/daemon/src/project-root.ts @@ -0,0 +1,23 @@ +import path from 'node:path'; + +export function resolveProjectRoot(moduleDir: string): string { + const base = path.basename(moduleDir); + const daemonDir = + base === 'dist' || base === 'src' ? path.dirname(moduleDir) : moduleDir; + return path.resolve(daemonDir, '../..'); +} + +export function resolveProjectRootFromNestedModule(moduleDir: string): string { + let current = path.resolve(moduleDir); + while (true) { + const base = path.basename(current); + if (base === 'dist' || base === 'src') { + return resolveProjectRoot(current); + } + const parent = path.dirname(current); + if (parent === current) { + return resolveProjectRoot(moduleDir); + } + current = parent; + } +} diff --git a/apps/daemon/src/project-routes.ts b/apps/daemon/src/project-routes.ts index 6977d6cb2..941c2e8fa 100644 --- a/apps/daemon/src/project-routes.ts +++ b/apps/daemon/src/project-routes.ts @@ -1,4 +1,5 @@ import type { Express } from 'express'; +import path from 'node:path'; import { defaultScenarioPluginIdForProjectMetadata, type PluginManifest, @@ -171,6 +172,25 @@ function buildSrcdocTransportShell(): string { `; } +function projectDetailResolvedDir( + projectsRoot: string, + project: any, + resolveProjectDir: ( + projectsRoot: string, + projectId: string, + metadata?: unknown, + opts?: { allowUnavailableSandboxImportedProject?: boolean }, + ) => string, +): string { + const baseDir = typeof project?.metadata?.baseDir === 'string' + ? path.normalize(project.metadata.baseDir) + : null; + if (baseDir && path.isAbsolute(baseDir)) return baseDir; + return resolveProjectDir(projectsRoot, project.id, project.metadata, { + allowUnavailableSandboxImportedProject: true, + }); +} + const URL_PREVIEW_SCROLL_BRIDGE = `