Skip to content

Add Claude Artifact URL wrappers#20

Merged
Christian-Katzmann merged 1 commit into
Christian-Katzmann:mainfrom
SohamKela:main
Jun 5, 2026
Merged

Add Claude Artifact URL wrappers#20
Christian-Katzmann merged 1 commit into
Christian-Katzmann:mainfrom
SohamKela:main

Conversation

@SohamKela
Copy link
Copy Markdown
Contributor

@SohamKela SohamKela commented Jun 4, 2026

Description

Adds Claude Artifact support through a URL-only app strategy. When a project config includes external_url, artifact_url, or url, app-it now builds a macOS .app that opens the hosted Claude Artifact URL directly in the native wrapper instead of starting a local dev server.

This keeps Claude authentication, artifact runtime APIs, storage, and Claude API usage inside Claude’s hosted artifact environment. The generated app does not distribute API keys, cookies, copied sessions, or shared authentication. Each person who receives the .app launches it, signs into Claude with their own account, and uses their own Claude subscription/plan.

Raw JSX/TSX artifact source is still treated as ordinary local web app source only when it does not depend on Claude-hosted runtime APIs such as window.claude, window.storage, MCP prompts, or Claude-provided auth. If it does depend on those APIs, the supported path is to publish/share the artifact in Claude and wrap that hosted URL.

Acceptance Criteria Coverage

  • .app packages can be created from existing Claude Artifacts by configuring a published/shared Claude Artifact URL via artifact_url, external_url, or url.
  • The generated .app launches locally and loads the hosted artifact in-window.
  • Claude auth and Claude API usage remain tied to the logged-in user inside the app window.
  • Distributed .app bundles do not include shared secrets; recipients must sign into Claude and use their own plan.
  • URL-only apps start no local daemon, write no server.port, and are handled correctly by desktop:doctor and desktop:quit.

Changes

  • Add Swift and Chrome URL-only run templates.
  • Add external_url / artifact_url / url parsing in desktop-build.sh and desktop-doctor.sh.
  • Add allow-external-hosts support to the Swift wrapper so Claude auth redirects, hosted iframes, and API bridge navigation stay inside the app window.
  • Update inspect.sh to detect Claude Artifact URLs and local source that references Claude Artifact runtime APIs.
  • Update docs and skill instructions with the security boundary for Claude Artifact packaging.
  • Add a fixture covering URL-only Claude Artifact app generation and smoke behavior.

Validation

  • ./scripts/validate.sh

Notes

This intentionally does not implement a local clone of Claude’s Artifact runtime. That would require credential/session/API shims and would not satisfy the requirement that every user consume the artifact through their own Claude plan.

Summary by Sourcery

Add support for building macOS URL-only app wrappers around hosted Claude Artifacts while preserving Claude-hosted auth and runtime behavior.

New Features:

  • Allow apps to be configured with external_url/artifact_url/url so desktop builds produce URL-only launchers that open hosted Claude Artifacts or other web apps without starting a local dev server.
  • Introduce Swift and Chrome URL-only run templates and extend the WebKit wrapper to keep hosted auth and API navigation inside the app window via an allow-external-hosts flag.

Enhancements:

  • Extend desktop doctor and quit scripts to understand URL-only apps, report their configuration, and skip local server/port diagnostics where not applicable.
  • Update inspection tooling to detect Claude Artifact URLs and runtime API usage and guide users toward URL-based packaging when Claude-hosted APIs are required.
  • Document the new Strategy E URL-only hosted app flow, its configuration, and its security boundaries around Claude authentication and API keys.
  • Broaden skill and project documentation to treat Claude Artifact URLs as first-class app-it targets and clarify distribution limitations.

Documentation:

  • Update SKILL, README, and compatibility docs to describe URL-only Claude Artifact wrappers, when to use them, and how they differ from local project packaging.

Tests:

  • Add a Claude Artifact URL fixture and smoke test to verify URL-only launcher behavior, template selection, and absence of local server state.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jun 4, 2026

Reviewer's Guide

Adds first-class support for URL-only macOS apps that wrap hosted Claude Artifacts, including new URL run templates, config parsing for external URLs, runtime tooling updates, tests, and documentation explaining the security boundary and behavior for these wrappers.

Sequence diagram for URL-only Claude Artifact app launch

sequenceDiagram
    actor User
    participant AppBundle as macOS_App
    participant RunScript as run-template-url.sh
    participant Wrapper as wrapper.swift
    participant Claude as claude.ai_artifact

    User->>AppBundle: Double-click .app
    AppBundle->>RunScript: Launch Contents/MacOS/run
    RunScript->>RunScript: Validate APP_URL is http(s)
    RunScript->>Wrapper: exec wrapper APP_URL APP_NAME "" "" POLYFILL_PATH allow-external-hosts
    Wrapper->>Claude: Load APP_URL in WKWebView
    Claude-->>Wrapper: Auth redirect / artifact UI
    Wrapper-->>Claude: allowExternalHosts keeps in-app navigation in-window
    User->>Claude: Sign into Claude
    Claude-->>User: Artifact runs inside native app window
Loading

File-Level Changes

Change Details Files
Add URL-only launcher templates and build-time selection logic for wrapping hosted Claude Artifact URLs without starting local servers.
  • Introduce Swift-based URL-only run template that calls the wrapper with an external URL, no port/pid, and an allow-external-hosts flag.
  • Add Chrome fallback URL-only run template that uses --app with a dedicated profile and no server.
  • Extend desktop-build.sh config parsing to read external_url/artifact_url/url and propagate it into the internal apps table.
  • Update desktop-build.sh to choose between single-server, multi-server, and URL-only templates, and substitute APP_URL when building URL-only apps.
  • Ensure validate.sh asserts presence of new URL-only run templates and that test fixtures cover URL-only artifact builds.
plugins/app-it/skills/app-it/templates/run-template-url.sh
plugins/app-it/skills/app-it/templates/run-template-url-chrome.sh
plugins/app-it/skills/app-it/templates/desktop-build.sh
scripts/validate.sh
scripts/test-fixtures.sh
scripts/fixtures/claude-artifact-url/app-it.config.json
Teach runtime tooling (doctor, quit, inspect) about URL-only apps so they skip server checks, surface URLs, and detect Claude Artifact usage correctly.
  • Extend desktop-doctor.sh to read external_url/artifact_url/url, mark URL-only apps, show their URL, and validate http(s) URLs.
  • Short-circuit desktop-doctor.sh runtime-port, PID, backend, PATH, and server-log checks for URL-only apps while still checking template drift against run-template-url.sh.
  • Update template-drift logic to probe different markers depending on URL vs local server run template, including APP_IT_SMOKE and allow-external-hosts for URL-only apps.
  • Adjust desktop-quit.sh config reader to normalize text fields and broaden wrapper process matching to cover non-localhost URL wrappers.
  • Enhance inspect.sh to scan for claude.ai URLs and Claude Artifact runtime APIs (window.claude/window.storage/etc.) and report guidance when found.
plugins/app-it/skills/app-it/templates/desktop-doctor.sh
plugins/app-it/skills/app-it/templates/desktop-quit.sh
plugins/app-it/skills/app-it/templates/inspect.sh
Extend Swift wrapper behavior to optionally keep external hosts in-window for URL-only artifacts while preserving existing behavior for local apps.
  • Update wrapper.swift usage contract to accept an allow-external-hosts flag argument for both app-it and app-it-static variants.
  • Thread an allowExternalHosts boolean into the Swift AppDelegate and store it alongside existing configuration fields.
  • Modify WKNavigationAction policy to always allow navigation when allowExternalHosts is true, keeping hosted auth redirects and iframes inside the app window.
  • Wire CLI parsing so the seventh argument equal to allow-external-hosts enables this behavior in both wrappers.
plugins/app-it/skills/app-it/templates/wrapper.swift
plugins/app-it-static/skills/app-it-static/templates/wrapper.swift
Clarify and extend skill docs and README to describe Strategy E URL-only apps, Artifact-specific guidance, and the security boundary (no shared auth/API keys).
  • Update SKILL.md description, principles, and trigger phrases to include hosted artifacts and add a new principle about keeping Claude Artifact auth with Claude.
  • Document artifact detection in inspect phase, expand runtime-shape taxonomy to include URL-only hosted apps, and introduce Strategy E with decision tree integration.
  • Describe config shape for URL-only apps, the APP_URL placeholder, and constraints on not shimming Claude APIs or sharing credentials.
  • Add a Strategy E section explaining runtime behavior, smoke testing, and distribution limitations for URL-only apps.
  • Update README and COMPATIBILITY docs to mention wrapping Claude Artifact URLs, local-first policy, and limits on redistributing Claude auth or keys.
plugins/app-it/skills/app-it/SKILL.md
docs/COMPATIBILITY.md
README.md
Align ancillary scripts and fixtures with URL-only behavior, including doctor, quit, and fixture coverage.
  • Ensure desktop-launcher.md.template and app-it.config.example.json (not fully shown) are compatible with external_url semantics.
  • Add a claude-artifact-url fixture project and config to exercise URL-only build and doctor behavior.
  • Update test-fixtures.sh to assert URL-only run script content, allow-external-hosts usage, smoke-run behavior, absence of server.port, and doctor messaging for URL-only apps.
plugins/app-it/skills/app-it/templates/app-it.config.example.json
plugins/app-it/skills/app-it/templates/desktop-launcher.md.template
scripts/test-fixtures.sh
scripts/fixtures/claude-artifact-url/app-it.config.json

Possibly linked issues

  • #0: The PR adds URL-only Claude Artifact app wrappers that satisfy the issue’s per-user-plan packaging and distribution requirements.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In the new URL-only launchers (run-template-url.sh and run-template-url-chrome.sh), the interpolated $APP_NAME and $APP_URL are passed directly into osascript alert strings; consider escaping quotes/newlines or using a here-doc AppleScript body to avoid malformed dialogs or potential injection if names/URLs contain unexpected characters.
  • For configs that accidentally specify both a local server (port/start_command/backend_*) and an external_url/artifact_url, the build and doctor flows silently prefer the URL-only path; it may be worth emitting an explicit warning in desktop-build.sh or desktop-doctor.sh when both modes are configured to make this precedence clear to users.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the new URL-only launchers (`run-template-url.sh` and `run-template-url-chrome.sh`), the interpolated `$APP_NAME` and `$APP_URL` are passed directly into `osascript` alert strings; consider escaping quotes/newlines or using a here-doc AppleScript body to avoid malformed dialogs or potential injection if names/URLs contain unexpected characters.
- For configs that accidentally specify both a local server (`port`/`start_command`/`backend_*`) and an `external_url`/`artifact_url`, the build and doctor flows silently prefer the URL-only path; it may be worth emitting an explicit warning in `desktop-build.sh` or `desktop-doctor.sh` when both modes are configured to make this precedence clear to users.

## Individual Comments

### Comment 1
<location path="plugins/app-it/skills/app-it/templates/inspect.sh" line_range="112-121" />
<code_context>
+if command -v rg >/dev/null 2>&1; then
</code_context>
<issue_to_address>
**suggestion:** Reduce duplication and brittle quoting in Artifact detection regexes

The `rg` and `grep` branches currently duplicate the same two regexes with slightly different quoting, which risks them drifting out of sync and makes the nested quoting brittle. Consider defining the patterns once (e.g., `ARTIFACT_URL_PATTERN`, `ARTIFACT_API_PATTERN`) and reusing them in both branches, or switching to double-quoted strings with explicit backslash escaping to simplify the character class and improve maintainability.

Suggested implementation:

```
print_section "Claude Artifact signals"

ARTIFACT_URL_PATTERN="https://claude\.ai/[^ )\"'>]+"
ARTIFACT_API_PATTERN="window\.claude|window\.storage|claude\.complete|claude\.request"

if command -v rg >/dev/null 2>&1; then

```

```
    ARTIFACT_URLS="$(rg --no-heading -n -E "$ARTIFACT_URL_PATTERN" \

```

```
    ARTIFACT_API="$(rg --no-heading -n -E "$ARTIFACT_API_PATTERN" \

```

```
    ARTIFACT_URLS="$(grep -RnE "$ARTIFACT_URL_PATTERN" . \

```

To fully remove duplication and brittle quoting, you should also:
1. Update any corresponding `grep`-based search for the artifact API signals (likely near the shown `ARTIFACT_URLS` grep) to use `"$ARTIFACT_API_PATTERN"` instead of an inlined regex.
2. Ensure any other occurrences of the same patterns elsewhere in the script are replaced with these two variables so that the regexes are defined in only one place.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread plugins/app-it/skills/app-it/templates/inspect.sh
Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in 3fe74cf:

  • Replaced direct osascript -e interpolation in both URL-only launchers with an AppleScript heredoc that receives title/message as argv, so quotes/newlines in app names or URLs do not break the dialog source.
  • Added explicit warnings when an external URL is configured alongside local server fields; URL-only mode still wins, but build/doctor now say so.
  • Consolidated Claude Artifact URL/API regexes in inspect.sh into shared pattern variables reused by both rg and grep paths.

Validation: ./scripts/validate.sh passed.

Copy link
Copy Markdown
Contributor Author

Resolved the upstream conflicts by rebasing the Claude Artifact URL support onto current main (5cc75ee) and force-updating the fork branch to 13ac829.

Notes:

  • Preserved upstream's newer port_mode, native run stub / run.sh, JSON doctor, and verification changes.
  • Re-applied URL-only launcher support on top of those newer templates.
  • Updated the split skill reference docs introduced upstream.

Validation: ./scripts/validate.sh passed locally after the rebase.

@Christian-Katzmann Christian-Katzmann merged commit 4de787f into Christian-Katzmann:main Jun 5, 2026
4 checks passed
Christian-Katzmann pushed a commit that referenced this pull request Jun 5, 2026
The merged URL-only launcher (#20) is provider-neutral in code, but the docs
framed it Claude-first. Neutralize the identity-defining surfaces — README
headline/positioning, SKILL.md description + title + the hosted-auth principle,
and the COMPATIBILITY heading — to "any hosted web app", keeping a published
Claude Artifact as the named concrete example (still the common case). No
behavior change; the mechanism is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Christian-Katzmann
Copy link
Copy Markdown
Owner

Merged — thank you, @SohamKela. This is your second solid contribution to app-it and it shows: the mechanism is clean, the security boundary (no shipped credentials, each user signs into their own account) is exactly right, and you'd already addressed every review note before I looked.

Two things worth calling out:

I verified it on real macOS hardware before merging. The fixtures-real CI job only runs on schedule/dispatch, so the URL-only path hadn't actually been exercised anywhere. I built the claude-artifact-url fixture and ran it end-to-end — build → APP_IT_SMOKE smoke → desktop-doctor (text and JSON) — all green, no server.port written, and confirmed the normal local builds still work with the new 11-field config record. The universal .app builds and the allow-external-hosts flag threads through correctly.

Small follow-up in c6ab3cc. Your code is already provider-neutral (external_url, a generic allow-external-hosts flag, any http(s) URL), but the docs framed the feature Claude-first. Since app-it is deliberately assistant-agnostic, I generalized the identity-defining surfaces — README headline/positioning, the SKILL description + the hosted-auth principle, and the COMPATIBILITY heading — to "any hosted web app," keeping a published Claude Artifact as the named concrete example (it's the common case). No behavior change; your mechanism is untouched.

You're credited in the README's Community nudge for the URL-only launcher. Appreciate the careful work. 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants