db48d1d261
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
db48d1d261 |
fix(canvas): restore text-white on saturated buttons + close zinc gaps
Independent code review of #2555 caught two contrast regressions left by the bulk perl pass: 1. text-white → text-ink mass-substitution silently broke destructive and primary buttons. text-ink resolves to #15181c (warm-paper near-black) in light mode — dark text on bg-red-600 / bg-amber-600 / bg-emerald-600 / bg-blue-600 / bg-accent / bg-accent-strong / bg-good / bg-bad fails WCAG contrast and looks broken. Per-line pass flips text-ink → text-white only when a saturated bg utility is present; tinted-state pills (bg-red-950/50 etc.) keep their intentionally-retained text-* literals. 2. Original mapping table was missing bg-zinc-600 (most-used hover-state literal for cancel buttons — caused them to JUMP from warm cream resting state to dark zinc on hover in light mode) and text-zinc-700/800/900 (separator dots and decorative dim text invisible on warm-paper light bg). Extended mapping fills these gaps with bg-surface-card / text-ink-soft. Also: drop stale tailwind.config.ts reference from components.json (file deleted by the v3→v4 migration); switch baseColor zinc → neutral and enable cssVariables since v4 uses CSS-driven tokens. Future shadcn-cli invocations would have failed or written malformed components without this. 27 sites in 27 files affected by #1, ~20 sites in 20 files by #2. 1214/1214 unit tests still pass; build still clean. Findings courtesy of multi-model review per code-review-and-quality skill — different blind spots catch different bugs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c0eca8d0e1 |
feat(canvas): warm-paper theme + Tailwind v4 migration
Brings the canvas onto the warm-paper design system already shipped to landing, marketplace, and SaaS surfaces, and migrates the build from Tailwind v3 → v4 to match molecule-app. Plumbing: - swap tailwindcss v3 → v4, drop autoprefixer, add @tailwindcss/postcss - delete tailwind.config.ts (v4 reads tokens from @theme blocks in CSS) - globals.css: @import "tailwindcss" + @plugin "@tailwindcss/typography" - two @theme blocks: warm-paper light defaults + always-dark surface tokens (bg-bg / ink-mute / line-strong) for terminal/console panels - [data-theme="dark"] cascade overrides the warm-paper tokens for dark - React Flow edge stroke + scrollbar + selection colour pull from semantic tokens so they flip with the theme Theme infra (ported from molecule-app, identical contracts): - lib/theme-cookie.ts: mol_theme cookie + boot script (no "use client" so server components can read the constants) - lib/theme-provider.tsx: ThemeProvider + useTheme + cookie writer with Domain=.moleculesai.app so the preference follows the user across canvas/app/market/landing subdomains AND tenant subdomains - lib/theme.ts: ColorToken union + cssVar() helper - components/ThemeToggle.tsx: 3-way System/Light/Dark picker - layout.tsx: SSR cookie read + nonce'd inline boot script (CSP needs the explicit nonce — strict-dynamic doesn't forgive an un-nonce'd inline sibling) + ThemeProvider wrapper + bg-surface/text-ink body Component migration (62 files): - Mechanical bg-zinc-* / text-zinc-* / border-zinc-* / text-white → semantic surface/ink/line tokens via perl negative-lookahead pass (preserves opacity modifiers like /80, /60) - bg-blue-500/600 → bg-accent / bg-accent-strong - text-red-* / amber-* / emerald-* → text-bad / warm / good - Tinted-state banner backgrounds (bg-red-950, bg-amber-950, bg-blue-950 etc.) intentionally left literal — they remain readable on warm-paper in light mode without inventing new state-soft tokens - TerminalTab.tsx skipped — xterm renders to canvas, not DOM - 3 unit-test assertions updated to match new token strings (credits pillTone, AuthGate overlay class, A2AEdge accent) Verification: - pnpm test: 1214/1214 pass - pnpm tsc --noEmit: clean - next build: ✓ Compiled successfully (8 routes) - dev server inspection: html data-theme stamped, body uses bg-surface text-ink, boot script carries nonce, compiled CSS contains both @theme blocks + [data-theme="dark"] override Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b54ceb799f |
fix: address 5-axis review findings on PR #2413
Critical:
- ExternalConnectModal.tsx: filledUniversalMcp substitution searched
for WORKSPACE_AUTH_TOKEN but the snippet's placeholder is now
MOLECULE_WORKSPACE_TOKEN (changed in the previous polish commit
|
||
|
|
876c0bfcd4 |
docs(canvas): update Universal MCP snippet — molecule-mcp now standalone
The canvas tab snippet for the Universal MCP path was written before this PR added the built-in register + heartbeat thread. Earlier wording described it as "outbound-only — pair with the Claude Code or Python SDK tab for heartbeat + inbound messages" — that's stale. molecule-mcp now handles register + heartbeat itself; the only thing it doesn't yet do is inbound A2A delivery. Updated: - externalUniversalMcpTemplate header comment + body — describes standalone behavior, points operators at SDK/channel only when they need INBOUND (not heartbeat). - Drops the now-redundant curl-register step from the snippet — the binary registers itself on startup. - Canvas modal label likewise updated. No runtime / behavior change; pure docs polish so a copy-pasting operator's mental model matches what the binary actually does. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
716589742c |
feat(canvas): add Universal MCP tab to external-agent connect modal
The "Connect your external agent" dialog already covered Claude Code, Python SDK, curl, and raw fields. This adds a Universal MCP tab that documents the new \`molecule-mcp\` console script — the runtime- agnostic baseline shipped by PR #2413's workspace-runtime changes. Surface area: - New \`externalUniversalMcpTemplate\` constant in workspace-server. Three-step snippet: pip install runtime → one-shot register via curl → wire molecule-mcp into agent's MCP config (Claude Code example, notes that hermes/codex/etc. take the same env-var contract). - Workspace create response now includes \`universal_mcp_snippet\` alongside the existing curl/python/channel snippets. - Canvas modal renders the tab when \`universal_mcp_snippet\` is present; backward-compatible with older platform builds (tab hides when empty). Origin/WAF coverage (the user explicitly asked for this): - The runtime wheel handles Origin automatically (this PR's earlier commit on platform_auth.auth_headers). - The curl tab now sets \`Origin: {{PLATFORM_URL}}\` preemptively with an explanatory comment; \`/registry/register\` is currently WAF-allowed without it but adding now keeps the snippet working if WAF rules expand. The comment also explains why \`/workspaces/*\` paths return empty 404 without Origin — the exact failure mode I hit while smoke-testing this PR live. - The MCP snippet's footer notes that the wheel auto-handles Origin so operators don't think about it. End-to-end verification (against live tenant hongmingwang.moleculesai.app, freshly registered workspace): - get_workspace_info → full JSON - list_peers → "Claude Code Agent (ID: 97ac32e9..., status: online)" - recall_memory → "No memories found." all returned by the molecule-mcp binary speaking MCP stdio to this Claude Code session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
240d513ab8 |
canvas(ExternalConnectModal): add Claude Code tab + auto-fill auth_token
When the platform's create-external-workspace response includes `claude_code_channel_snippet` (added in this same PR's first commit), the modal surfaces it as the **first** tab — defaulting to it for new external workspaces because polling-based + no-tunnel is the lowest- friction path. Falls back to Python tab when the field is absent (older platform builds). Type addition is optional (`claude_code_channel_snippet?: string`) so the canvas keeps building against pre-#2304 platform responses during the soak window. Auth-token stamping mirrors existing python/curl behavior — the .env's `MOLECULE_WORKSPACE_TOKENS=<paste auth_token from create response>` placeholder gets filled in client-side so the copy-paste block is truly ready to run. Also adds the missing 'use client' directive — the file uses useState + useCallback but didn't have the Next.js client-component marker. Pre-commit caught it; existing absence was a latent bug that would surface as an SSR hook error if any path rendered this component during server rendering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
808cc5437f |
fix(canvas): ExternalConnectModal redundant null check on Dialog.Root open prop
[Molecule-Platform-Evolvement-Manager] Addresses github-code-quality finding on PR #2064: > Comparison between inconvertible types > Variable 'info' cannot be of type null, but it is compared to > an expression of type null. By line 75, `info` has been narrowed to non-null via the `if (!info) return null;` guard at line 56 — so `open={info !== null}` always evaluates to `true`. Switch to JSX shorthand `open` for clarity and to silence the static check. Behaviorally identical; the modal still opens whenever the parent renders this component (which only happens with non-null info). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1e8b5e0167 |
feat(external-runtime): first-class BYO-compute workspaces + manifest-driven registry
## Problem
Two issues the external-workspace path was silently dropping:
1. `knownRuntimes` was a hardcoded Go map that drifted from
manifest.json — e.g. `gemini-cli` was in manifest but missing
from the Go allowlist, so any workspace provisioning with
runtime=gemini-cli got silently coerced to langgraph.
2. No end-to-end "bring your own compute" story. The canvas UI
had no way to pick runtime=external; the partial backend code
required the operator to already have a URL ready (chicken-and-
egg with the agent that doesn't exist yet), and no workspace_auth
_token was minted so the external agent couldn't authenticate its
register call.
## Change
### Runtime registry driven by manifest.json
- New `runtime_registry.go` reads `manifest.json` at service init.
Each `workspace_templates[].name` becomes a runtime identifier
(with the `-default` suffix stripped so `claude-code-default`
and `claude-code` resolve to the same runtime).
- `external` is always injected (no template repo exists for it).
- Falls back to a static map on manifest load failure so tests /
dev containers keep working.
- 5 new tests including a real-manifest sanity check.
### First-class external workspace flow
When `POST /workspaces` is called with `runtime: "external"` AND
no URL supplied:
1. Workspace row inserted with `status='awaiting_agent'`
(distinct from `provisioning` so canvas doesn't trip its
provisioning-timeout UX).
2. A workspace_auth_token is minted via `wsauth.IssueToken`.
3. Response body includes a `connection` object with:
- `workspace_id`, `platform_url`, `auth_token`
- `registry_endpoint`, `heartbeat_endpoint`
- `curl_register_template` — zero-dep one-shot register snippet
- `python_snippet` — full SDK setup w/ heartbeat loop,
paired with molecule-sdk-python PR #13's A2AServer
4. The platform URL is resolved from `EXTERNAL_PLATFORM_URL` env
(ops-configurable per tenant) or falls back to request headers.
The legacy `payload.External` + `payload.URL` path is preserved —
org-import and other callers that already have a URL still work.
### Canvas UI
- New "External agent (bring your own compute)" checkbox in
CreateWorkspaceDialog.
- When checked, template/model/hermes-provider fields are hidden
and the POST body includes `runtime: "external"`.
- New `ExternalConnectModal` component: shown once after create,
renders Python / curl / raw-fields tabs with copy-to-clipboard
buttons. Stays mounted as a sibling of the create dialog so the
token survives the create dialog unmount.
- `auth_token` is interpolated into the snippet client-side so the
copied block is truly ready to run — operator only has to fill
in their agent's public URL.
## Tests
- Go: 5 new runtime_registry tests (happy path, -default strip,
external always injected, missing file, malformed JSON, real
manifest sanity). All existing handler tests still pass.
- TypeScript: no type errors on my files; pre-existing
canvas-batch-partial-failure type drift is on main already and
tracked on the #2061 branch.
## Follow-ups (filed separately)
- Cut molecule-sdk-python v0.y to PyPI so the snippet can use
`pip install molecule-ai-sdk` instead of `git+main`.
- Add a `runtime: string` field per template in manifest.json so
one template can declare its runtime explicitly (instead of
deriving it from name conventions). Unblocks N-templates-per-
runtime (e.g. hermes-minimax, hermes-anthropic both runtime=hermes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|