From e0023503c54cb4c1614f81b571129a1d90184c30 Mon Sep 17 00:00:00 2001 From: 0thernet Date: Sat, 15 Aug 2026 05:23:41 -0400 Subject: [PATCH 01/10] refactor: rename transmute to atet --- .github/workflows/ci.yml | 12 +- .github/workflows/release.yml | 6 +- .github/workflows/vectorizer.yml | 4 +- AGENTS.md | 13 +- CONTRIBUTING.md | 2 +- NOTICE.md | 6 +- README.md | 411 ++++++++---------- SECURITY.md | 4 +- .../{transmute.config.ts => atet.config.ts} | 2 +- examples/capex-opex.diagram.json | 2 +- examples/declarative-workflow.ts | 12 +- examples/render-workflow.ts | 14 +- examples/semantic-flow.diagram.json | 14 +- package.json | 31 +- schema/diagram.schema.json | 2 +- scripts/check-standalone.ts | 120 ++++- scripts/official-vectorizer-smoke.ts | 6 +- scripts/package-smoke.ts | 34 +- scripts/release-workflow.test.ts | 22 +- scripts/validate-schema.ts | 7 + scripts/validate-skill.ts | 6 +- skills/{transmute => atet}/SKILL.md | 46 +- skills/{transmute => atet}/agents/openai.yaml | 4 +- .../references/customization.md | 10 +- .../references/reference-led-3d.md | 12 +- .../references/visual-communication.md | 2 +- tsconfig.json | 26 +- 27 files changed, 451 insertions(+), 379 deletions(-) rename examples/{transmute.config.ts => atet.config.ts} (87%) rename skills/{transmute => atet}/SKILL.md (87%) rename skills/{transmute => atet}/agents/openai.yaml (69%) rename skills/{transmute => atet}/references/customization.md (86%) rename skills/{transmute => atet}/references/reference-led-3d.md (94%) rename skills/{transmute => atet}/references/visual-communication.md (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75930b8..5da942d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,7 +81,7 @@ jobs: "$sdk" "$desktop" "$native" "$package" "$site" | tee -a "$GITHUB_OUTPUT" boundary: - name: Standalone boundary + name: Atet standalone boundary runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -94,7 +94,7 @@ jobs: - run: bun run check:standalone sdk: - name: SDK + name: Atet SDK needs: plan if: needs.plan.outputs.sdk == 'true' runs-on: ubuntu-latest @@ -120,7 +120,7 @@ jobs: - run: node --input-type=module -e 'await Promise.all(["./dist/index.js","./dist/cli.js","./dist/code/index.js","./dist/code/advanced.js","./dist/generate.js","./dist/host-resources.js","./dist/operations.js","./dist/workflow.js"].map(path => import(path)))' desktop: - name: Local host + name: Atet local runtime needs: plan if: needs.plan.outputs.desktop == 'true' runs-on: ubuntu-latest @@ -147,7 +147,7 @@ jobs: fi site: - name: Static site + name: Atet site needs: plan if: needs.plan.outputs.site == 'true' runs-on: ubuntu-latest @@ -163,7 +163,7 @@ jobs: - run: bun run check:web package: - name: Packed consumer + name: Atet packed consumer needs: plan if: needs.plan.outputs.package == 'true' runs-on: ubuntu-latest @@ -178,7 +178,7 @@ jobs: - run: bun run test:package native: - name: macOS native shell + name: Atet macOS shell needs: plan if: needs.plan.outputs.native == 'true' runs-on: macos-15 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 927e4a7..12a05c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: - run: node --input-type=module -e 'await Promise.all(["./dist/index.js", "./dist/cli.js", "./dist/code/index.js", "./dist/code/advanced.js", "./dist/generate.js", "./dist/host-resources.js", "./dist/operations.js", "./dist/workflow.js"].map((specifier) => import(specifier)))' official_vtracer: - name: Official VTracer (${{ matrix.target }}) + name: Atet VTracer (${{ matrix.target }}) needs: verify permissions: contents: read @@ -115,7 +115,7 @@ jobs: EXPECTED_ARCH: ${{ matrix.arch }} native_macos: - name: macOS native shell + name: Atet macOS shell needs: verify permissions: contents: read @@ -187,7 +187,7 @@ jobs: --verify-tag \ --generate-notes \ --latest \ - --title "Transmute $GITHUB_REF_NAME" + --title "Atet $GITHUB_REF_NAME" fi release_state="$(gh release view "$GITHUB_REF_NAME" \ --json assets,isDraft,isImmutable,isPrerelease,tagName \ diff --git a/.github/workflows/vectorizer.yml b/.github/workflows/vectorizer.yml index d2064e9..1233b6c 100644 --- a/.github/workflows/vectorizer.yml +++ b/.github/workflows/vectorizer.yml @@ -1,4 +1,4 @@ -name: Official VTracer +name: Atet VTracer on: push: @@ -10,7 +10,7 @@ permissions: jobs: official_vtracer: - name: Official VTracer (${{ matrix.target }}) + name: Atet VTracer (${{ matrix.target }}) runs-on: ${{ matrix.runner }} timeout-minutes: 15 strategy: diff --git a/AGENTS.md b/AGENTS.md index fa6759e..5236ddd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,12 @@ # Contents - `src/` – portable declarative and imperative workflow SDKs, host-resource admission, diagrams, direct Vercel AI Gateway generation, local vectorization, semantic operations, MCP, and canvas integration. -- `apps/desktop/` – the canonical `transmute` CLI, complete local media host, durable scheduler, HTML and Three.js overlays, native capture helpers, frontend, and desktop shell. -- `apps/web/` – the dependency-free static `transmute.rocks` site, with no API, account, or credential surface. +- `apps/desktop/` – the canonical `atet` CLI, complete local media host, durable scheduler, HTML and Three.js overlays, native capture helpers, frontend, and desktop shell. +- `apps/web/` – the dependency-free static `atet.sh` site, with no API, account, or credential surface. - `packages/scene/` – the shared local scene-analysis contract. - `src/code/` – portable declarative graph authoring, the closed public capability projection, compilation, planning, and execution contracts. - `schema/` – version-one diagram JSON Schema. -- `skills/transmute/` – the canonical Transmute Agent Skill. +- `skills/atet/` – the canonical Atet Agent Skill. - `examples/` – checked diagram, configuration, and executable imperative and declarative Bun workflow examples. - `scripts/` – schema, skill, package, release, and official-vectorizer verification. - `dist/` and `apps/desktop/dist/cli/` – committed Bun-targeted entrypoints consumed by package and Git installs. @@ -17,13 +17,14 @@ # Guidelines - Use Bun 1.3.14 and run `bun run check` before handing off a change. -- Keep `@hraness/transmute` at the repository root. The root package owns both the portable SDK and canonical `transmute` binary built from `apps/desktop/cli/main.ts`. -- Keep canonical commands namespaced as `transmute diagram init|check|render`, `transmute image generate|vectorize`, and the `code`, `mcp`, `canvas`, `skill`, and `doctor` surfaces. +- Keep `@hraness/atet` at the repository root. The root package owns both the portable SDK and canonical `atet` binary built from `apps/desktop/cli/main.ts`. +- Keep canonical commands namespaced as `atet diagram init|check|render`, `atet image generate|vectorize`, and the `code`, `mcp`, `canvas`, `skill`, and `doctor` surfaces. - Keep local vectorization authentication-free and network-silent. Gateway generation reads `AI_GATEWAY_API_KEY` before `VERCEL_OIDC_TOKEN`, never persists credentials, pins the Gateway origin, bounds responses, and sets `maxRetries: 0`. - Keep `/artifacts/`, `.env`, and `.env.*` ignored. Recordings, imported media, private project metadata, Gateway tokens, and provider options must never enter Git or a package artifact. - Treat `vercel env run -- ` as the ergonomic local Vercel path. Never shell out to infer or scrape a token from the Vercel CLI. - Preserve `.diagram.json` version one and the five same-stem render outputs: `.tldr`, light and dark SVG, and light and dark PNG. -- Keep one self-contained Transmute identity. Do not add hosted accounts, OAuth, an application database, a credential store, or legacy Graphics runtime aliases. +- Keep one self-contained Atet identity. Version 2 retains `transmute` only as a one-major CLI bin alias and accepts only reviewed version-1 serialized identifiers at explicit compatibility boundaries. Do not retain predecessor branding, package names, source names, or runtime implementations. +- Preserve every immutable version-1 tag and Release. Never rewrite a historical tag or recreate a repository at the former GitHub path, because doing so would replace GitHub's compatibility redirect. - Treat diagram source as authoritative and generated media as replaceable. Defaults may resolve mechanics but must not invent claims, labels, legends, relationships, or decorative meaning. - Parse foreign values from `unknown`, keep output deterministic, and test every parser, protocol, operation, path, credential, and compatibility boundary. - Keep semantic registries fixed and typed. Never accept source text, evaluation, dynamic imports, executable workspace configuration, shell commands, or caller-selected remote URLs. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f95be8b..c2588a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ bun run check:web Parser, layout, operation, protocol, configuration, or scheduler changes need deterministic examples. Add a property test for a law, round trip, ordering rule, or arbitrary-input boundary. A shrunk property failure should become a named regression. -Keep the portable declarative graph SDK canonical in `src/code/`. The complete local host extends that fixed model under `apps/desktop/`; it does not maintain a competing graph contract. Public local-host entrypoints use `@hraness/transmute/local/*`. +Keep the portable declarative graph SDK canonical in `src/code/`. The complete local host extends that fixed model under `apps/desktop/`; it does not maintain a competing graph contract. Public local-host entrypoints use `@hraness/atet/local/*`. Generation uses Vercel AI Gateway directly. Do not add an account service, session store, custom OAuth flow, hosted proxy, billing dependency, or browser credential field. Tests must use inert credentials and controlled transports. diff --git a/NOTICE.md b/NOTICE.md index cfe33cb..b824d56 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,6 +1,6 @@ # Notices -Transmute is an independent project and is not affiliated with or endorsed by +Atet is an independent project and is not affiliated with or endorsed by tldraw, Inc. The runtime package does not include the tldraw SDK or tldraw Offline. It writes @@ -9,7 +9,7 @@ diagram into compatible tldraw software. The development test suite optionally uses the upstream `tldraw` package to check compatibility; that package remains under the [tldraw license](https://tldraw.dev/community/license). -The optional `transmute canvas install` command downloads an unmodified installer +The optional `atet canvas install` command downloads an unmodified installer from the official [`tldraw/tldraw-offline`](https://github.com/tldraw/tldraw-offline) GitHub release and verifies the SHA-256 digest published by GitHub. tldraw Offline is @@ -40,5 +40,5 @@ Optional model-backed generation uses [`@ai-sdk/gateway`](https://www.npmjs.com/package/@ai-sdk/gateway) and the [`ai`](https://www.npmjs.com/package/ai) SDK. Prompts and referenced media are sent to Vercel AI Gateway and the model provider selected by the caller, under -those services' terms. Transmute does not operate an intermediary generation +those services' terms. Atet does not operate an intermediary generation service. diff --git a/README.md b/README.md index 737c685..6842f54 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,95 @@ -# Transmute +# Atet -Transmute is an agent tool for generating and editing images and video. This -package is its open-source TypeScript SDK and Bun CLI for turning checked -source into visual assets. It provides deterministic diagrams, light and dark -raster/vector exports, editable tldraw interchange, bounded raster-to-SVG -conversion, direct Vercel AI Gateway image generation, typed -Bun-script workflows, semantic operation dispatch, and a local MCP server. +[![Atet: an open visual-media toolkit for agents and people](https://atet.sh/og.png)](https://atet.sh) -The package is designed to compose with editors and video renderers. Its portable declarative graph SDK is the canonical workflow core used by the restricted public host and the complete local host. The desktop shell is a native capture and UI client of that local host, not a second workflow engine. A diagram, generated image, traced SVG, or `.tldr` canvas remains an ordinary media input rather than a format trapped inside the tool. +**Carry an idea all the way into view.** -Project site: [transmute.rocks](https://transmute.rocks) +Open-source TypeScript SDK, Bun CLI, and local runtime for turning ideas and raw assets into images, diagrams, animated loops, and video. + +Atet is named for Ra's solar barque. The name fits the work: one vessel carries +an idea from its first rough material through rendering, review, and delivery. +Atet keeps that journey inspectable, with explicit sources, bounded execution, +and editable artifacts instead of an opaque creative endpoint. + +```sh +bun add --global github:hraness/atet#v2.0.0 +atet doctor +atet diagram init diagrams/system.diagram.json +atet image vectorize input.png --output input.svg --json +``` + +[Project site](https://atet.sh) · [Security policy](SECURITY.md) · [Architecture](docs/architecture.md) + +## What Atet makes + +- **Images.** Generate through Vercel AI Gateway or turn caller-owned raster + artwork into bounded, inert SVG with an exact provenance receipt. +- **Diagrams.** Author checked JSON, then render editable tldraw interchange, + light and dark SVG, and light and dark PNG from the same source. +- **Animated loops and video.** Compose local project graphs from imported + media, generated candidates, HTML or Three.js overlays, audio, captions, + camera moves, and explicit delivery variants. +- **Typed visual workflows.** Build declarative graphs or ordinary Bun + workflows over a closed operation registry, with resource claims and durable + receipts at every execution boundary. + +## Why Atet is different + +- **Local custody.** Project state, imported media, generated candidates, and + delivery artifacts stay in caller-owned storage. Model requests go directly + from the current process to Vercel AI Gateway. +- **Source before output.** Diagram source, scene source, workflow graphs, and + exact references remain authoritative. Rendered media is replaceable. +- **Bounded by construction.** Parsers, paths, pixels, frames, responses, + subprocesses, downloads, and concurrent resource claims have explicit limits. +- **Honest artifacts.** Atet records the tool, input identity, model, output, + and verification evidence needed to understand how a result was made. +- **One visual engine.** The SDK, CLI, MCP server, complete local runtime, and + desktop shell share the same typed contracts instead of drifting into + separate products. + +Atet complements editors, model providers, agent frameworks, and render tools. +Those systems own their interfaces and models. Atet owns the checked path from +an idea or source asset to visual-media artifacts an agent and a person can +inspect together. ## Install -Pin the public repository to the immutable `v1.0.0` tag: +Atet requires Bun 1.3.14. Pin the public repository to the immutable `v2.0.0` +tag for a global CLI install: ```sh -bun add --global github:hraness/transmute#v1.0.0 -transmute doctor +bun add --global github:hraness/atet#v2.0.0 +atet doctor ``` -For programmatic use: +Install the SDK into a Bun project with the same immutable source: ```sh -bun add github:hraness/transmute#v1.0.0 +bun add github:hraness/atet#v2.0.0 ``` -Transmute requires Bun 1.3.14. Diagram rendering works on macOS, Linux, and Windows. Bounded VTracer execution works on macOS and Linux; Windows fails closed with `tool_platform` until its output can cross the same bounded capture path. Semantic operation and workflow resource admission is machine-global on macOS and Linux and process-local on other supported Bun platforms. +Atet is distributed through immutable GitHub tags and Releases. It is not +published to npm. + +Diagram rendering runs on macOS, Linux, and Windows. Bounded VTracer execution +runs on macOS and Linux; Windows fails closed with `tool_platform` until its +output can cross the same bounded capture path. Machine-global resource +admission is available on macOS and Linux and falls back truthfully to +process-local admission elsewhere. -## Create a diagram +## Diagrams ```sh -transmute diagram init diagrams/system.diagram.json -transmute diagram check diagrams/system.diagram.json --strict -transmute diagram render diagrams/system.diagram.json +atet diagram init diagrams/system.diagram.json +atet diagram check diagrams/system.diagram.json --strict +atet diagram render diagrams/system.diagram.json ``` -The checked source uses `.diagram.json` version one. Each render replaces the same five derivatives: +A successful render writes five same-stem outputs: ```text +system.diagram.json system.tldr system.light.svg system.dark.svg @@ -46,15 +97,11 @@ system.light.png system.dark.png ``` -The source remains authoritative. The five outputs are replaceable and each file is published through an atomic rename. The five-file family is not a filesystem transaction. - -Use this schema URL in authored files: - -The version-one diagram schema is unchanged in 1.0.0, so its canonical identity remains the `v0.8.0` URL. +The checked source uses the versioned public schema: ```json { - "$schema": "https://raw.githubusercontent.com/hraness/transmute/v0.8.0/schema/diagram.schema.json", + "$schema": "https://raw.githubusercontent.com/hraness/atet/v2.0.0/schema/diagram.schema.json", "version": 1, "name": "source-result", "canvas": { "width": 960, "height": 540 }, @@ -72,120 +119,80 @@ The version-one diagram schema is unchanged in 1.0.0, so its canonical identity } ``` -Array order may place shapes, but it never creates relationships. Edges stay explicit. Impossible stack constraints fail instead of silently shrinking shapes, gaps, or the canvas. +Place `atet.config.ts`, `.mjs`, `.js`, or `.json` beside a diagram to provide +local fonts, sanitized SVG icons, or theme colors. Pass `--config ` when +the configuration belongs elsewhere. MCP and semantic operations never load +executable workspace configuration. -Place `transmute.config.ts`, `.mjs`, `.js`, or `.json` beside a diagram to supply local fonts, sanitized SVG icons, or theme colors. Pass `--config ` when configuration belongs elsewhere. MCP and semantic operations deliberately ignore executable workspace configuration. +## Generate and vectorize images -## Vectorize a local image +Local vectorization is authentication-free and network-silent: ```sh -transmute image vectorize input.png --output input.svg --json -transmute image vectorize input.png \ +atet image vectorize input.png --output input.svg --json +atet image vectorize input.png \ --output input.duotone.svg \ - --duotone '#171717,#7c3aed' -``` - -Canonical vectorization requires no credential and makes no network request. It bounds encoded input, decoded dimensions and pixels, subprocess time, paths, emitted bytes, and measured fidelity. The output is rebuilt as inert SVG geometry; foreign tracer SVG is not passed through. - -VTracer 0.6.4 downloads from a checksum-pinned official release on first use. `TRANSMUTE_VTRACER_PATH` may point to a compatible local binary, whose hash is recorded in the receipt. `TRANSMUTE_CACHE_DIR` overrides the default tool cache. - -Programmatic use returns the SVG, published path, and provenance receipt: - -```ts -import { vectorizeImage } from "@hraness/transmute" - -const result = await vectorizeImage("input.png", { - outputPath: "input.svg", -}) - -console.log(result.receipt.sourceSha256, result.receipt.svgSha256) + --duotone '#1f2937,#f97316' \ + --json ``` -## Generate an image through Vercel AI Gateway - -Generation goes directly from your process to Vercel AI Gateway. Transmute has -no account, OAuth, database, or hosted generation service of its own. +Atet downloads the checksum-pinned official VTracer 0.6.4 archive on first +use. `ATET_VTRACER_PATH` can select a compatible local binary and +`ATET_CACHE_DIR` can move the tool cache. The receipt records the exact binary +hash and trace measurements. -```sh -export AI_GATEWAY_API_KEY=your_key -transmute image generate 'one cobalt circle on white' \ - --output circle.webp -``` - -If the repository is linked to a Vercel project, inject its environment without -writing a local dotenv file: +Model-backed generation reads `AI_GATEWAY_API_KEY` before +`VERCEL_OIDC_TOKEN`, pins the Gateway origin, sets `maxRetries: 0`, and never +stores or prints either credential: ```sh -vercel link -vercel env run -- transmute image generate \ - 'a polished metallic monogram on transparent black' \ - --model openai/gpt-image-1.5 \ - --output monogram.webp +export AI_GATEWAY_API_KEY='replace-me' +atet image generate 'one cobalt circle on white' \ + --model google/gemini-3.1-flash-image-preview \ + --output circle.webp \ + --json ``` -`AI_GATEWAY_API_KEY` takes precedence over `VERCEL_OIDC_TOKEN`. Vercel -deployments supply the latter automatically; `vercel env run` can supply it to -local commands. Transmute never stores, accepts on argv, or prints either -credential. It pins `https://ai-gateway.vercel.sh/v4/ai`, rejects redirects, -bounds streamed responses, requests one image with `maxRetries: 0`, validates -PNG, JPEG, or WebP signatures, and publishes with atomic no-replace semantics. - -## Use semantic operations - -The registry has four canonical codes: - -- `transmute.diagram.check` -- `transmute.diagram.render` -- `transmute.image.vectorize` -- `transmute.image.generate` +With a linked Vercel project, inject a short-lived OIDC token without creating +a project dotenv file: ```sh -transmute code search diagram --limit 4 -transmute code execute transmute.diagram.check \ - --input '{"path":"diagrams/system.diagram.json"}' +vercel env run -- atet image generate \ + 'one cobalt circle on white' \ + --output circle.webp \ + --json ``` -Search returns bounded descriptors. Execute accepts strict JSON for an exact registered code. It does not accept source text, shell commands, dynamic imports, executable configuration, or caller-selected remote URLs. Direct SDK, CLI, and MCP execution acquires the operation's declared host-resource claims before work begins. +## SDK and workflows -The same surface is available from `@hraness/transmute/operations`: +Importing an SDK entrypoint has no CLI side effect and does not inspect local +state: ```ts -import { - executeTransmuteOperation, - searchTransmuteOperations, -} from "@hraness/transmute/operations" +import { vectorizeImage } from "@hraness/atet" -const matches = searchTransmuteOperations("diagram") -const result = await executeTransmuteOperation("transmute.diagram.check", { - path: "diagrams/system.diagram.json", +const result = await vectorizeImage("input.png", { + outputPath: "input.svg", }) -``` - -## Build a declarative workflow graph - -`@hraness/transmute/code` is the additive declarative SDK. It builds a typed graph, compiles that graph against the closed public capability projection, produces a deterministic plan and requirement envelope, then runs accepted operation nodes through the existing semantic executor and host-resource boundary. - -The public projection contains exactly the four diagram and image operations listed above. Workflow code cannot register another capability. A graph that names a local-host-only operation fails compilation with `unsupported-plan` before an executor or resource coordinator is called. - -See [`examples/declarative-workflow.ts`](examples/declarative-workflow.ts) for a standalone Bun module that checks and renders a diagram. Run it in a checkout with: -```sh -bun run examples/declarative-workflow.ts examples/capex-opex.diagram.json +console.log(result.receipt.sourceSha256, result.receipt.svgSha256) ``` -`@hraness/transmute/code/advanced` exposes the portable graph, reference, projection, policy, compiler, plan, and runner contracts for hosts that need lower-level integration. It does not expose a mutable global registry, a capability-registration hook, complete local-host bindings, or built-in local workflows. +`@hraness/atet/code` builds typed declarative graphs. It compiles each graph +against the host's closed capability projection and rejects unsupported work +before resource admission. `@hraness/atet/code/advanced` exposes lower-level +graph, compiler, plan, policy, and runner contracts without adding a mutable +operation registry. -## Use the v0.8 imperative workflow API - -`@hraness/transmute/workflow` composes the same fixed, typed operation registry in an ordinary Bun script. A definition parses its runtime input before work begins. Step ids are unique, execution is bounded to 64 steps by default, and the run result records completed steps in invocation order even when branches settle in a different order. +`@hraness/atet/workflow` composes the same fixed operations in explicitly +imported Bun code. Runtime input is parsed before work starts, step identifiers +are unique, execution is bounded, and completed-step receipts survive every +failure path. ```ts -import { - defineTransmuteWorkflow, - runTransmuteWorkflow, -} from "@hraness/transmute/workflow" +import { defineAtetWorkflow, runAtetWorkflow } from "@hraness/atet/workflow" -const checkedRender = defineTransmuteWorkflow({ +const checkedRender = defineAtetWorkflow({ id: "checked-render", version: 1, parseInput(value: unknown) { @@ -197,146 +204,92 @@ const checkedRender = defineTransmuteWorkflow({ async run(workflow, input) { const checked = await workflow.operation( "check", - "transmute.diagram.check", + "atet.diagram.check", input, ) const rendered = await workflow.operation( "render", - "transmute.diagram.render", + "atet.diagram.render", input, ) return { checked, artifacts: rendered.artifacts } }, }) -const result = await runTransmuteWorkflow(checkedRender, { +const result = await runAtetWorkflow(checkedRender, { path: "diagrams/system.diagram.json", }) console.log(result.steps, result.output.artifacts) ``` -See [`examples/render-workflow.ts`](examples/render-workflow.ts) for an executable script. Run it in a checkout with: - -```sh -bun run examples/render-workflow.ts examples/capex-opex.diagram.json -``` - -Parallel branches are ordinary `Promise.all` calls around `workflow.operation(...)`. Every step code and input is validated against the fixed public operation registry before dispatch, including when a custom executor is injected. The runner drains every dispatched operation before it returns, including a branch that authored code did not await, so destructive work cannot escape a successful run. `AbortSignal` is checked before dispatch and after each built-in operation completes. Custom injected executors also receive the signal for cooperative in-flight cancellation. A failed parallel step snapshots the steps completed at that moment; already-started siblings are not implicitly cancelled and may still settle afterward. Errors thrown by authored workflow code use `WORKFLOW_FAILED` and retain the completed-step receipt. +The complete local runtime extends these portable contracts with immutable +source revisions, generated candidates, selections, media timelines, durable +scheduling, HTML and Three.js overlays, native capture helpers, and delivery +variants. The desktop shell adds operating-system permissions and application +UI; ordinary SDK and CLI use does not require it. -Every semantic operation declares physical resource claims. The default -profile reserves CPU headroom and bounds local I/O, FFmpeg, video encoding, -Vision, Whisper, browser, network, paid-call, and capture work. On macOS and -Linux, independent Bun processes and Git worktrees enter the same crash-safe -FIFO admission boundary. A terminated process releases its kernel-backed -lease, while an admitted subprocess inherits the lease descriptor so it cannot -outlive the claimed capacity. Direct SDK, CLI, MCP, and workflow calls share -this boundary. Windows uses the same profile within one process. +See [Architecture](docs/architecture.md) for the full boundary map. -Use `@hraness/transmute/host-resources` to inspect or inject the boundary: - -```ts -import { - createDefaultHostResourceCoordinator, - defaultTransmuteHostResourceProfile, -} from "@hraness/transmute/host-resources" -import { runTransmuteWorkflow } from "@hraness/transmute/workflow" - -const hostResourceCoordinator = createDefaultHostResourceCoordinator({ - profile: defaultTransmuteHostResourceProfile(), -}) - -await runTransmuteWorkflow(workflow, input, { hostResourceCoordinator }) -``` - -`runTransmuteWorkflow` also accepts `signal` and -`waitTimeoutMilliseconds` beside the coordinator. The same three controls may -be supplied in `dependencies` when one dependency object is shared with direct -operation calls; explicit workflow-level values take precedence. - -Admission cancellation applies while waiting. Once a callback owns capacity, -the coordinator retains that capacity until the callback actually settles, -even if higher-level workflow cancellation has already returned an error. -Custom executors receive the exact `hostResourceLease`; subprocess launchers -must inherit its descriptor and call `assertOwned()` before irreversible work. - -Workflow modules are trusted current-user Bun code that you explicitly import and run. Module initialization has the same authority as other code run by the current user. The declarative compiler restricts operation nodes to the supplied closed capability projection and rejects unsupported capabilities before executor or resource admission. The SDK does not load arbitrary paths, evaluate source strings, add operation codes, or expose an open registration hook. - -## Continue reference-led work in the complete local host - -Image-to-Three.js scenes and reference-led metallic logo treatments use -Transmute's complete local Code Mode host in this repository. The host consumes -the same canonical SDK and adds the durable media runtime. The desktop shell -adds native capture, operating-system permission flows, and application UI. -Those capabilities are outside the portable four-operation projection. The -local host composes -`gateway.image`, reviewed HTML scene source, the exact Three.js browser lock, -and `media.htmlOverlay` in its typed project graph. - -The staged Three.js flow is reference generation, source authoring, review, -full-length 1x preview, then selected final render. The exact reference artifact -remains bound to the scene as provenance. Model-produced JavaScript is never an -operation input. The local-host starter supplies explicit color and tone policy, -camera fitting, draw-call and triangle ceilings, deterministic orbit, zoom and -explode parameters, shader precompilation, and GPU cleanup. - -Metallic logo generation stays an image treatment. Its typed local-host recipe -takes an exact logo reference, brand name, background color, object color, and -explicit model. The reference remains the shape authority, and the output -remains a candidate until silhouette, negative space, proportions, and lettering -pass review. - -The packaged Agent Skill contains the complete procedure in -`skills/transmute/references/reference-led-3d.md`. If only this public package is -installed, generate the source image here and continue in the complete local -host. Local Bun scripts and CLI commands do not require the desktop shell; the -shell is needed only for native capture, permissions, and application UI. Do -not add Three.js as a dependency or evaluate scene source through the fixed -public operation registry. - -## Connect MCP +## Semantic operations and MCP ```sh -transmute mcp --root /absolute/path/to/workspace +atet operations list --json +atet code search diagram --limit 4 +atet code execute atet.diagram.check \ + --input '{"path":"diagrams/system.diagram.json","strict":true}' +atet mcp --root /absolute/path/to/workspace ``` -The stdio server exposes `check_diagram`, `render_diagram`, `search_transmute`, and `execute_transmute`. File arguments are root-relative and confined to the selected workspace. Diagram source, shape count, edge count, PNG pixels, returned findings, paths, and outputs are bounded before execution. The server is a trusted local workspace boundary, not an operating-system sandbox against concurrent same-user mutation. +The stdio server exposes dedicated diagram tools plus `search_atet` and +`execute_atet`. File arguments are root-relative and confined to the selected +workspace. The server is a trusted local workspace boundary, not an +operating-system sandbox against concurrent same-user mutation. -## Work with canvases +## Canvases and the Agent Skill -Diagram rendering does not require the tldraw SDK or desktop app. The generated `.tldr` file is editable interchange: +Generated `.tldr` files are editable interchange and do not require the tldraw +SDK or desktop app to render: ```sh -transmute canvas open diagrams/system.tldr -transmute canvas status -transmute canvas url -transmute canvas install +atet canvas open diagrams/system.tldr +atet canvas status +atet canvas install +atet skill install --target codex --scope user ``` -The optional installer flow resolves an official tldraw Offline release, verifies its published SHA-256 digest, and prepares the platform installer. A native `.tldraw` file is an app-owned ZIP/SQLite bundle; Transmute opens it but does not rewrite it directly. - -## Install the Agent Skill - -```sh -transmute skill install --target codex --scope user -transmute skill install --target agents --scope project -``` - -The skill keeps literal prompts, checked diagram source, rendering, vectorization, semantic operations, and review steps together. `transmute skill path` prints its packaged location. - -## Command reference - -| Command | Result | -| --- | --- | -| `transmute diagram init [file]` | Create a starter without overwriting an existing file. | -| `transmute diagram check ` | Parse and lint a version-one diagram source. | -| `transmute diagram render ` | Replace `.tldr`, light/dark SVG, and light/dark PNG derivatives. | -| `transmute image vectorize ` | Trace one local raster to bounded inert SVG without authentication. | -| `transmute image generate ` | Generate one validated image directly through Vercel AI Gateway. | -| `transmute code search|execute` | Search or execute the fixed semantic registry. | -| `transmute mcp --root ` | Serve confined tools over stdio. | -| `transmute canvas open|status|url|install` | Inspect or use optional canvas integration. | -| `transmute skill path|install` | Locate or install the packaged Agent Skill. | -| `transmute doctor` | Report runtime, vectorizer, Gateway environment, MCP, and canvas status. | +The optional installer resolves an official tldraw Offline release and verifies +its published SHA-256 digest. The packaged Agent Skill keeps literal prompts, +checked source, exact references, rendering, vectorization, semantic operations, +and review in one reusable workflow. `atet skill path` prints its packaged +location. + +## Compatibility in version 2 + +Version 2 makes Atet the repository, package, SDK, skill, site, and canonical +CLI identity. The former `transmute` executable remains an alias to `atet` +through the 2.x release line so existing scripts have one major version to +migrate. Version-1 serialized operation identifiers and MCP tool names remain +accepted only as compatibility inputs; new output and documentation use Atet +identifiers. + +The immutable version-1 tags and Releases remain available in the same GitHub +repository history. GitHub redirects the former repository URL after the +rename. Do not create a new repository at that old path, because doing so would +replace GitHub's redirect. + +## Limits and trust + +- Generated meaning is never inferred into diagram labels, claims, legends, + or relationships. Defaults resolve mechanics only. +- Raster and vector inputs, output pixels, arrays, frames, durations, + subprocesses, downloads, and responses are bounded before execution. +- Explicitly imported workflow modules are trusted current-user Bun code. Atet + does not load caller-selected modules or evaluate source strings. +- Model-backed generation sends the prompt and supplied media to Vercel AI + Gateway and the chosen provider. Local diagram rendering and vectorization do + not require that network authority. +- The complete local runtime treats generated media as candidates until an + explicit selection is bound to a delivery revision. ## Development @@ -345,8 +298,12 @@ bun install --frozen-lockfile --ignore-scripts bun run check ``` -`bun run check` typechecks, validates the schema and skill, runs deterministic and property tests, builds the public entrypoints, and installs the resulting archive in a clean consumer. +`bun run check` verifies the standalone boundary, typechecks and lints the SDK +and local runtime, validates the schema and Agent Skill, runs deterministic and +property tests, builds committed entrypoints, checks the static site, and +installs the packed archive in a clean consumer. ## License -MIT. See [NOTICE.md](NOTICE.md) for optional tldraw Offline and VTracer integration terms. +MIT. See [NOTICE.md](NOTICE.md) for tldraw Offline, VTracer, rendering, and model +integration terms. diff --git a/SECURITY.md b/SECURITY.md index 4d49c87..8494dba 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,9 +1,9 @@ # Security -Report suspected vulnerabilities privately through [GitHub’s security advisory form](https://github.com/hraness/transmute/security/advisories/new). Do not open a public issue for credential exposure, path escape, unsafe SVG output, archive extraction, subprocess containment, unbounded resource use, Gateway authority substitution, or MCP boundary failures. +Report suspected vulnerabilities privately through [GitHub’s security advisory form](https://github.com/hraness/atet/security/advisories/new). Do not open a public issue for credential exposure, path escape, unsafe SVG output, archive extraction, subprocess containment, unbounded resource use, Gateway authority substitution, or MCP boundary failures. Include the affected version, platform, command or API, minimal reproduction, expected boundary, observed result, and whether any secret or caller-owned media left the machine. Remove tokens, account identifiers, private paths, and proprietary media from the report. -Transmute’s local MCP server confines paths to one caller-selected root but is not an operating-system sandbox against concurrent same-user mutation. Vectorization is network-silent. Generation sends its prompt and explicitly supplied media directly to Vercel AI Gateway. Transmute reads `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` from the process environment and never persists either value. +Atet’s local MCP server confines paths to one caller-selected root but is not an operating-system sandbox against concurrent same-user mutation. Vectorization is network-silent. Generation sends its prompt and explicitly supplied media directly to Vercel AI Gateway. Atet reads `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` from the process environment and never persists either value. An explicitly imported workflow module is trusted Bun code with the authority of the current user. Review workflow modules before running them. Declarative graph compilation restricts operation nodes to the closed capability projection supplied by the host and rejects unsupported capabilities before executor or resource admission, but it does not sandbox the JavaScript that constructs the graph. diff --git a/examples/transmute.config.ts b/examples/atet.config.ts similarity index 87% rename from examples/transmute.config.ts rename to examples/atet.config.ts index a36043c..47dcc16 100644 --- a/examples/transmute.config.ts +++ b/examples/atet.config.ts @@ -1,4 +1,4 @@ -import type { DiagramConfig } from "@hraness/transmute" +import type { DiagramConfig } from "@hraness/atet" export default { icons: { diff --git a/examples/capex-opex.diagram.json b/examples/capex-opex.diagram.json index cde3324..a73d587 100644 --- a/examples/capex-opex.diagram.json +++ b/examples/capex-opex.diagram.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/hraness/transmute/v0.8.0/schema/diagram.schema.json", + "$schema": "https://raw.githubusercontent.com/hraness/atet/v2.0.0/schema/diagram.schema.json", "version": 1, "name": "capex-opex", "canvas": { diff --git a/examples/declarative-workflow.ts b/examples/declarative-workflow.ts index fc6c660..e6af175 100644 --- a/examples/declarative-workflow.ts +++ b/examples/declarative-workflow.ts @@ -1,12 +1,12 @@ import { buildWorkflow, compileWorkflowGraph, - createTransmuteCodeHost, + createAtetCodeHost, defineWorkflow, PUBLIC_WORKFLOW_REGISTRY_PROJECTION, runBuiltWorkflow, -} from "@hraness/transmute/code" -import { executeTransmuteOperation } from "@hraness/transmute/operations" +} from "@hraness/atet/code" +import { executeAtetOperation } from "@hraness/atet/operations" import { z } from "zod" const DeclarativeWorkflowInputSchema = z.strictObject({ @@ -17,7 +17,7 @@ const DeclarativeWorkflowInputSchema = z.strictObject({ export const declarativeCheckedRenderWorkflow = defineWorkflow({ id: "declarative-checked-render", inputSchema: DeclarativeWorkflowInputSchema, - inputSchemaId: "transmute.example.declarative-checked-render.input/v1", + inputSchemaId: "atet.example.declarative-checked-render.input/v1", version: 1, build(builder, input) { const checked = builder.diagram.check("check-source", { @@ -58,8 +58,8 @@ if (import.meta.main) { const planned = compileWorkflowGraph({ graph: built.graph, }) - const host = createTransmuteCodeHost({ - execute: async request => await executeTransmuteOperation( + const host = createAtetCodeHost({ + execute: async request => await executeAtetOperation( request.kind, request.input, ), diff --git a/examples/render-workflow.ts b/examples/render-workflow.ts index e0bd054..34f28c3 100644 --- a/examples/render-workflow.ts +++ b/examples/render-workflow.ts @@ -1,7 +1,7 @@ import { - defineTransmuteWorkflow, - runTransmuteWorkflow, -} from "@hraness/transmute/workflow" + defineAtetWorkflow, + runAtetWorkflow, +} from "@hraness/atet/workflow" interface RenderWorkflowInput { readonly path: string @@ -26,14 +26,14 @@ function parseInput(value: unknown): RenderWorkflowInput { } } -export const checkedRenderWorkflow = defineTransmuteWorkflow({ +export const checkedRenderWorkflow = defineAtetWorkflow({ id: "checked-render", version: 1, parseInput, async run(workflow, input) { const checked = await workflow.operation( "check-source", - "transmute.diagram.check", + "atet.diagram.check", { path: input.path }, ) if (checked.findings.length > 0) { @@ -41,7 +41,7 @@ export const checkedRenderWorkflow = defineTransmuteWorkflow({ } const rendered = await workflow.operation( "render-assets", - "transmute.diagram.render", + "atet.diagram.render", { path: input.path, ...(input.outDirectory === undefined @@ -61,7 +61,7 @@ if (import.meta.main) { if (path === undefined) { throw new Error("Usage: bun run examples/render-workflow.ts [out-dir]") } - const result = await runTransmuteWorkflow(checkedRenderWorkflow, { + const result = await runAtetWorkflow(checkedRenderWorkflow, { path, ...(Bun.argv[3] === undefined ? {} : { outDirectory: Bun.argv[3] }), }) diff --git a/examples/semantic-flow.diagram.json b/examples/semantic-flow.diagram.json index d5f5ccf..4055a7f 100644 --- a/examples/semantic-flow.diagram.json +++ b/examples/semantic-flow.diagram.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/hraness/transmute/v0.8.0/schema/diagram.schema.json", + "$schema": "https://raw.githubusercontent.com/hraness/atet/v2.0.0/schema/diagram.schema.json", "version": 1, "name": "semantic-flow", "canvas": { @@ -20,12 +20,12 @@ "tone": "purple" }, { - "id": "transmute", + "id": "atet", "type": "rect", "width": 220, "height": 168, "radius": 28, - "label": "Transmute", + "label": "Atet", "tone": "blue" }, { @@ -39,13 +39,13 @@ ], "edges": [ { - "id": "prompt-transmute", + "id": "prompt-atet", "from": "prompt", - "to": "transmute" + "to": "atet" }, { - "id": "transmute-artifact", - "from": "transmute", + "id": "atet-artifact", + "from": "atet", "to": "artifact" } ] diff --git a/package.json b/package.json index 59dbe48..1ecffa5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "@hraness/transmute", - "version": "1.0.0", - "description": "An open-source visual-media SDK and local Bun runtime for images, diagrams, animation, and video.", + "name": "@hraness/atet", + "version": "2.0.0", + "description": "Open-source TypeScript SDK, Bun CLI, and local runtime for turning ideas and raw assets into images, diagrams, animated loops, and video.", "license": "MIT", "type": "module", "sideEffects": [ @@ -54,17 +54,25 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/hraness/transmute.git" + "url": "git+https://github.com/hraness/atet.git" }, - "homepage": "https://transmute.rocks/", + "homepage": "https://atet.sh/", "bugs": { - "url": "https://github.com/hraness/transmute/issues" + "url": "https://github.com/hraness/atet/issues" }, "keywords": [ + "atet", "visual-media", + "creative-tools", + "typescript", + "bun", + "cli", + "local-first", "image-generation", + "image-vectorization", "diagram", "animation", + "animated-loops", "video", "threejs", "tldraw", @@ -128,6 +136,7 @@ } }, "bin": { + "atet": "./apps/desktop/dist/cli/main.js", "transmute": "./apps/desktop/dist/cli/main.js" }, "main": "./dist/index.js", @@ -181,9 +190,9 @@ "build": "bun run build:sdk && bun run build:desktop && bun run build:web", "build:sdk": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./dist\", { recursive: true, force: true })' && bun build ./src/index.ts ./src/cli.ts ./src/code/index.ts ./src/code/advanced.ts ./src/generate.ts ./src/host-resources.ts ./src/operations.ts ./src/workflow.ts ./src/vectorize/worker.ts --outdir ./dist --root ./src --target bun --format esm --splitting --packages external", "build:desktop": "bun run build:desktop:cli && bun run build:desktop:frontend && bun run build:desktop:runtime", - "build:desktop:cli": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./apps/desktop/dist/cli\", { recursive: true, force: true })' && bun build --target=bun --minify --sourcemap=none --packages external --external @hraness/transmute/cli apps/desktop/cli/main.ts --outdir apps/desktop/dist/cli", + "build:desktop:cli": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./apps/desktop/dist/cli\", { recursive: true, force: true })' && bun build --target=bun --minify --sourcemap=none --packages external --external @hraness/atet/cli apps/desktop/cli/main.ts --outdir apps/desktop/dist/cli", "build:desktop:frontend": "vite build --config apps/desktop/frontend/vite.config.ts && bun run check:desktop:direct-boundary", - "build:desktop:runtime": "bun build --compile --minify --sourcemap=none apps/desktop/runtime/src/main.ts --outfile apps/desktop/runtime/dist/transmute-gateway && bun run check:desktop:direct-boundary", + "build:desktop:runtime": "bun build --compile --minify --sourcemap=none apps/desktop/runtime/src/main.ts --outfile apps/desktop/runtime/dist/atet-gateway && bun run check:desktop:direct-boundary", "build:web": "bun run --cwd apps/web build", "dev:frontend": "vite --config apps/desktop/frontend/vite.config.ts", "dev:desktop": "bun run build:desktop:runtime && bun run ./apps/desktop/runtime/run-native.ts dev", @@ -203,10 +212,10 @@ "start:desktop:macos": "bun run build:desktop:capture:macos && bun run build:desktop:analysis:macos && bun run build:desktop:runtime && bun run ./apps/desktop/runtime/run-native.ts run", "package:desktop:macos": "bun run build:desktop:capture:macos && bun run build:desktop:analysis:macos && bun run build:desktop:frontend && bun run build:desktop:runtime && bun run ./apps/desktop/runtime/run-zig.ts build package -Dplatform=macos -Dpackage-target=macos && bun run ./apps/desktop/runtime/package-macos.ts", "build:cli:macos": "bun run ./apps/desktop/cli/build-compiled.ts", - "test:cli:compiled:macos": "bun run build:cli:macos && TRANSMUTE_RUN_COMPILED_CLI_SMOKE=1 bun test ./apps/desktop/cli/compiled-portable-surface.macos.test.ts", + "test:cli:compiled:macos": "bun run build:cli:macos && ATET_RUN_COMPILED_CLI_SMOKE=1 bun test ./apps/desktop/cli/compiled-portable-surface.macos.test.ts", "test:html-overlay": "bun test ./apps/desktop/html-overlay ./apps/desktop/application/html-overlay-browser-runtime.test.ts ./apps/desktop/application/html-overlay-integrity.test.ts ./apps/desktop/application/operations/media/html-overlay.test.ts ./apps/desktop/cli/html-overlay-renderer.security.test.ts ./apps/desktop/cli/html-overlay-snapshot-scavenger.test.ts ./apps/desktop/cli/html-overlay-renderer.macos.test.ts", - "test:html-overlay:browser:macos": "TRANSMUTE_RUN_HTML_OVERLAY_RENDERER_SMOKE=1 bun test ./apps/desktop/cli/html-overlay-renderer.macos.test.ts", - "test:html-overlay:operation:macos": "TRANSMUTE_RUN_HTML_OVERLAY_OPERATION_SMOKE=1 bun test ./apps/desktop/application/operations/media/html-overlay.test.ts", + "test:html-overlay:browser:macos": "ATET_RUN_HTML_OVERLAY_RENDERER_SMOKE=1 bun test ./apps/desktop/cli/html-overlay-renderer.macos.test.ts", + "test:html-overlay:operation:macos": "ATET_RUN_HTML_OVERLAY_OPERATION_SMOKE=1 bun test ./apps/desktop/application/operations/media/html-overlay.test.ts", "verify:html-overlay:macos": "bun run test:html-overlay:browser:macos && bun run test:html-overlay:operation:macos", "test:capture:hardware:macos": "bun test ./apps/desktop/capture/hardware-smoke.macos.test.ts", "doctor:macos": "native doctor --manifest apps/desktop/app.zon --strict", diff --git a/schema/diagram.schema.json b/schema/diagram.schema.json index 3e71357..61fcd90 100644 --- a/schema/diagram.schema.json +++ b/schema/diagram.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/hraness/transmute/v0.8.0/schema/diagram.schema.json", + "$id": "https://raw.githubusercontent.com/hraness/atet/v2.0.0/schema/diagram.schema.json", "title": "Diagram source", "description": "A positioned diagram or a coordinate-free stack diagram. Both source forms resolve to the positioned DiagramSpec used by renderers.", "oneOf": [ diff --git a/scripts/check-standalone.ts b/scripts/check-standalone.ts index e4bca1d..b65441e 100644 --- a/scripts/check-standalone.ts +++ b/scripts/check-standalone.ts @@ -66,28 +66,71 @@ const TEXT_EXTENSIONS = new Set([ const FORBIDDEN_SOURCE = [ { label: "private Jungle package", pattern: /@jungle\//u }, - { label: "private Jungle source path", pattern: /projects\/transmute/u }, + { label: "private Jungle source path", pattern: /projects\/(?:atet|transmute)/u }, { label: "private Jungle fixture path", pattern: /\/(?:tmp|work)\/jungle\//u }, { label: "Convex runtime", pattern: /(?:^|[^a-z])convex(?:[^a-z]|$)/iu }, { label: "Better Auth runtime", pattern: /better-auth/iu }, { label: "Suite Accounts runtime", pattern: /suite[-_ ]accounts/iu }, { label: "hosted account service", pattern: /account\.hraness\.com/iu }, - { label: "hosted Transmute API", pattern: /transmute\.rocks\/api/iu }, + { label: "legacy hosted API", pattern: /transmute\.rocks\/api/iu }, { label: "legacy Graphics runtime", pattern: /graphics-compat/iu }, { label: "duplicate compatibility schema alternative", - pattern: /z\.literal\("((?:transmute|studio)(?:\.[^"]+)?)"\),\s*z\.literal\("\1"\)/u, + pattern: /z\.literal\("((?:atet|transmute|studio)(?:\.[^"]+)?)"\),\s*z\.literal\("\1"\)/u, }, { label: "duplicate compatibility type alternative", - pattern: /"((?:transmute|studio)(?:\.[^"]+)?)"\s*\|\s*"\1"/u, + pattern: /"((?:atet|transmute|studio)(?:\.[^"]+)?)"\s*\|\s*"\1"/u, }, { label: "duplicate compatibility reader branch", - pattern: /!==\s*"((?:transmute|studio)(?:\.[^"]+)?)"[^;\n]{0,200}!==\s*"\1"/u, + pattern: /!==\s*"((?:atet|transmute|studio)(?:\.[^"]+)?)"[^;\n]{0,200}!==\s*"\1"/u, }, ]; +const LEGACY_IDENTITY = + /@hraness\/transmute|github\.com\/hraness\/transmute|transmute\.rocks|(?:Transmute|transmute|TRANSMUTE)/u; +const REVIEWED_SERIALIZED_COMPATIBILITY = [ + /\btransmute\.[a-z][a-z0-9.-]*(?:\.[a-z][a-z0-9.-]*)+\b/gu, + /\b(?:execute|search)_transmute\b/gu, +] as const; +const REVIEWED_FILE_COMPATIBILITY = new Map([ + [ + "AGENTS.md", + [/Version 2 retains `transmute` only as a one-major CLI bin alias/gu], + ], + [ + "README.md", + [/The former `transmute` executable remains an alias to `atet`/gu], + ], + [ + "package.json", + [/"transmute": "\.\/apps\/desktop\/dist\/cli\/main\.js"/gu], + ], + [ + "scripts/package-smoke.ts", + [ + /pattern: \/projects\\\/\(\?:atet\|transmute\)\/u/gu, + /pattern: \/transmute\\\.rocks\\\/api\/iu/gu, + /join\(consumer, "node_modules", "\.bin", "transmute"\)/gu, + ], + ], +]); + +function removeReviewedLegacyCompatibility( + path: string, + source: string, +): string { + let remaining = source; + for (const pattern of REVIEWED_SERIALIZED_COMPATIBILITY) { + remaining = remaining.replaceAll(pattern, ""); + } + for (const pattern of REVIEWED_FILE_COMPATIBILITY.get(path) ?? []) { + remaining = remaining.replaceAll(pattern, ""); + } + return remaining; +} + function extension(path: string): string { const match = /\.[^./]+$/u.exec(path); return match?.[0] ?? ""; @@ -162,29 +205,78 @@ const files = (await Promise.all(SCANNED_ROOTS.map(async root => { const sourceProblems: string[] = []; for (const file of files) { - if ( - file === import.meta.path - || file === join(ROOT, "scripts", "package-smoke.ts") - ) continue; + if (file === import.meta.path) continue; if (relative(ROOT, file) === "scripts/check-standalone.ts") continue; const rootRelative = relative(ROOT, file); + if (LEGACY_IDENTITY.test(rootRelative)) { + sourceProblems.push(`${rootRelative} retains a pre-Atet source path`); + } if ( !TEXT_EXTENSIONS.has(extension(file)) && !SCANNED_ROOT_FILES.includes(rootRelative) ) continue; const text = await readFile(file, "utf8"); - for (const rule of FORBIDDEN_SOURCE) { - if (rule.pattern.test(text)) { - sourceProblems.push(`${relative(ROOT, file)} contains ${rule.label}`); + if (rootRelative !== "scripts/package-smoke.ts") { + for (const rule of FORBIDDEN_SOURCE) { + if (rule.pattern.test(text)) { + sourceProblems.push(`${relative(ROOT, file)} contains ${rule.label}`); + } } } + const unreviewedIdentity = removeReviewedLegacyCompatibility(rootRelative, text); + if (LEGACY_IDENTITY.test(unreviewedIdentity)) { + sourceProblems.push( + `${rootRelative} contains an unreviewed pre-Atet identity outside serialized or CLI compatibility`, + ); + } } const problems = [...packageProblems, ...sourceProblems]; const rootPackage = await readJson(join(ROOT, "package.json")); +const expectedDescription = "Open-source TypeScript SDK, Bun CLI, and local runtime for turning ideas and raw assets into images, diagrams, animated loops, and video."; +if (rootPackage.name !== "@hraness/atet") { + problems.push("package.json name must be @hraness/atet"); +} +if (rootPackage.description !== expectedDescription) { + problems.push("package.json description does not match the canonical Atet description"); +} +if (rootPackage.homepage !== "https://atet.sh/") { + problems.push("package.json homepage must be https://atet.sh/"); +} +const repository = rootPackage.repository; +if ( + repository === null + || typeof repository !== "object" + || Array.isArray(repository) + || Reflect.get(repository, "url") !== "git+https://github.com/hraness/atet.git" +) { + problems.push("package.json repository must be hraness/atet"); +} +const bugs = rootPackage.bugs; +if ( + bugs === null + || typeof bugs !== "object" + || Array.isArray(bugs) + || Reflect.get(bugs, "url") !== "https://github.com/hraness/atet/issues" +) { + problems.push("package.json bugs URL must be the hraness/atet issue tracker"); +} +const bins = rootPackage.bin; +if ( + bins === null + || typeof bins !== "object" + || Array.isArray(bins) + || Object.keys(bins).sort().join(",") !== "atet,transmute" + || Reflect.get(bins, "atet") !== "./apps/desktop/dist/cli/main.js" + || Reflect.get(bins, "transmute") !== "./apps/desktop/dist/cli/main.js" +) { + problems.push("package.json bins must expose canonical atet plus the version-2 transmute alias"); +} const packageVersion = rootPackage.version; if (typeof packageVersion !== "string") { problems.push("package.json version must be a string"); +} else if (packageVersion !== "2.0.0") { + problems.push("package.json version must be 2.0.0 for the Atet identity cutover"); } else { const versionContracts = [ ["apps/desktop/app.zon", `.version = ${JSON.stringify(packageVersion)}`], @@ -195,7 +287,7 @@ if (typeof packageVersion !== "string") { ], [ "apps/desktop/cli/commands.ts", - `export const TRANSMUTE_VERSION = ${JSON.stringify(packageVersion)}`, + `export const ATET_VERSION = ${JSON.stringify(packageVersion)}`, ], [ "apps/desktop/cli/recording-controller.ts", @@ -203,7 +295,7 @@ if (typeof packageVersion !== "string") { ], [ "apps/desktop/runtime/package-macos.ts", - `transmute-${packageVersion}-macos-ReleaseFast.app`, + `atet-${packageVersion}-macos-ReleaseFast.app`, ], ] as const; for (const [path, expected] of versionContracts) { diff --git a/scripts/official-vectorizer-smoke.ts b/scripts/official-vectorizer-smoke.ts index 1cd6b1f..e737638 100644 --- a/scripts/official-vectorizer-smoke.ts +++ b/scripts/official-vectorizer-smoke.ts @@ -47,8 +47,8 @@ invariant( `Runner is ${process.platform}/${process.arch}, expected ${expectedPlatform}/${expectedArch}.`, ) invariant( - process.env.TRANSMUTE_VTRACER_PATH === undefined, - "Official VTracer smoke must not use TRANSMUTE_VTRACER_PATH.", + process.env.ATET_VTRACER_PATH === undefined, + "Official VTracer smoke must not use ATET_VTRACER_PATH.", ) const releaseKey = `${process.platform}-${process.arch}` @@ -67,7 +67,7 @@ invariant( `VTracer release metadata changed for ${releaseKey}.`, ) -const work = await mkdtemp(join(tmpdir(), "transmute-official-vtracer-")) +const work = await mkdtemp(join(tmpdir(), "atet-official-vtracer-")) try { // Generated in memory, so this smoke fixture carries no third-party rights. const raster = await sharp({ diff --git a/scripts/package-smoke.ts b/scripts/package-smoke.ts index 1d667e2..1a05a62 100644 --- a/scripts/package-smoke.ts +++ b/scripts/package-smoke.ts @@ -11,7 +11,7 @@ import { import { tmpdir } from "node:os"; import { basename, join, sep } from "node:path"; -const packageName = "@hraness/transmute"; +const packageName = "@hraness/atet"; const importSpecifiers = [ packageName, `${packageName}/cli`, @@ -71,13 +71,13 @@ const packageTextExtensions = new Set([ ]); const forbiddenPackageText = [ { label: "private package", pattern: /@jungle\//u }, - { label: "private source path", pattern: /projects\/transmute/u }, + { label: "private source path", pattern: /projects\/(?:atet|transmute)/u }, { label: "private fixture path", pattern: /\/(?:tmp|work)\/jungle\//u }, { label: "account database runtime", pattern: /(?:^|[^a-z])convex(?:[^a-z]|$)/iu }, { label: "hosted auth runtime", pattern: /better-auth/iu }, { label: "hosted account runtime", pattern: /suite[-_ ]accounts/iu }, { label: "hosted account origin", pattern: /account\.hraness\.com/iu }, - { label: "hosted Transmute API", pattern: /transmute\.rocks\/api/iu }, + { label: "legacy hosted API", pattern: /transmute\.rocks\/api/iu }, { label: "legacy Graphics runtime", pattern: /graphics-compat/iu }, ] as const; @@ -168,7 +168,7 @@ function record(value: unknown, label: string): Record { } const repository = process.cwd(); -const work = await mkdtemp(join(tmpdir(), "transmute-package-smoke-")); +const work = await mkdtemp(join(tmpdir(), "atet-package-smoke-")); try { const archive = join(work, "package.tgz"); const consumer = join(work, "consumer"); @@ -188,7 +188,7 @@ try { ); await run([process.execPath, "add", archive, "--ignore-scripts"], consumer); const installedPackage = await realpath( - join(consumer, "node_modules", "@hraness", "transmute"), + join(consumer, "node_modules", "@hraness", "atet"), ); await scanPackedPackage(installedPackage); await run([ @@ -196,16 +196,20 @@ try { "-e", `await Promise.all(${JSON.stringify(importSpecifiers)}.map(specifier => import(specifier)))`, ], consumer); + await run([ + join(consumer, "node_modules", ".bin", "atet"), + "--help", + ], consumer); await run([ join(consumer, "node_modules", ".bin", "transmute"), "--help", ], consumer); const doctorText = await runOutput([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "doctor", "--json", ], consumer); - const doctor = record(JSON.parse(doctorText) as unknown, "transmute doctor --json"); + const doctor = record(JSON.parse(doctorText) as unknown, "atet doctor --json"); const consumerRoot = await realpath(consumer); if (doctor.repositoryRoot !== consumerRoot) { throw new Error( @@ -222,20 +226,20 @@ try { ); } const operationsText = await runOutput([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "operations", "list", "--json", ], consumer); const operations = record( JSON.parse(operationsText) as unknown, - "transmute operations list --json", + "atet operations list --json", ).operations; if (!Array.isArray(operations) || operations.length === 0) { throw new Error("Packed CLI returned no local operations."); } const semanticSearchText = await runOutput([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "code", "search", "--limit", @@ -243,13 +247,13 @@ try { ], consumer); const semanticOperations = record( JSON.parse(semanticSearchText) as unknown, - "transmute code search --limit 1", + "atet code search --limit 1", ).operations; if (!Array.isArray(semanticOperations) || semanticOperations.length !== 1) { throw new Error("Packed CLI did not delegate semantic code search."); } const skillPath = (await runOutput([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "skill", "path", ], consumer)).trim(); @@ -258,15 +262,15 @@ try { throw new Error(`Packed CLI resolved a skill outside its install: ${skillPath}`); } const canvasStatus = record(JSON.parse(await runOutput([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "canvas", "status", - ], consumer)) as unknown, "transmute canvas status"); + ], consumer)) as unknown, "atet canvas status"); if (!("installedPath" in canvasStatus) || !("server" in canvasStatus)) { throw new Error("Packed CLI did not delegate canvas status."); } await runFailure([ - join(consumer, "node_modules", ".bin", "transmute"), + join(consumer, "node_modules", ".bin", "atet"), "mcp", ], consumer, "--root is required"); await run([ diff --git a/scripts/release-workflow.test.ts b/scripts/release-workflow.test.ts index b344550..14d31e3 100644 --- a/scripts/release-workflow.test.ts +++ b/scripts/release-workflow.test.ts @@ -29,16 +29,16 @@ function isMissingFile(error: unknown): boolean { ) } -test("public CI routes independent SDK, local-host, site, and native proofs", async () => { +test("public CI routes independent Atet SDK, local-runtime, site, and native proofs", async () => { const workflow = await readWorkflow("public-ci.yml", "ci.yml") expect(workflow).toContain("plan:\n name: Plan") - expect(workflow).toContain("boundary:\n name: Standalone boundary") - expect(workflow).toContain("sdk:\n name: SDK") - expect(workflow).toContain("desktop:\n name: Local host") - expect(workflow).toContain("site:\n name: Static site") - expect(workflow).toContain("package:\n name: Packed consumer") - expect(workflow).toContain("native:\n name: macOS native shell") + expect(workflow).toContain("boundary:\n name: Atet standalone boundary") + expect(workflow).toContain("sdk:\n name: Atet SDK") + expect(workflow).toContain("desktop:\n name: Atet local runtime") + expect(workflow).toContain("site:\n name: Atet site") + expect(workflow).toContain("package:\n name: Atet packed consumer") + expect(workflow).toContain("native:\n name: Atet macOS shell") expect(workflow).toContain("if: needs.plan.outputs.sdk == 'true'") expect(workflow).toContain("if: needs.plan.outputs.desktop == 'true'") expect(workflow).toContain("if: needs.plan.outputs.site == 'true'") @@ -61,7 +61,7 @@ test("public CI routes independent SDK, local-host, site, and native proofs", as expect(workflow).toContain("needs: [plan, boundary, sdk, desktop, site, package, native]") expect(workflow).toContain('[[ "$result" == success || "$result" == skipped ]]') expect(workflow).not.toContain(`@${"jungle"}/`) - expect(workflow).not.toContain(["projects", "transmute"].join("/")) + expect(workflow).not.toContain(["projects", "atet"].join("/")) }) test("version tags pass the complete immutable release gate", async () => { @@ -89,7 +89,7 @@ test("version tags pass the complete immutable release gate", async () => { expect(workflow).toContain("bun install --frozen-lockfile --ignore-scripts") expect(workflow).toContain("bun run check") expect(workflow).toContain("sudo apt-get install --yes ffmpeg") - expect(workflow).toContain("native_macos:\n name: macOS native shell") + expect(workflow).toContain("native_macos:\n name: Atet macOS shell") expect(workflow).toContain("bun run test:desktop:macos") expect(workflow).toContain("bun run package:desktop:macos") expect(workflow).toContain( @@ -110,6 +110,7 @@ test("version tags pass the complete immutable release gate", async () => { expect(workflow).toContain("--verify-tag") expect(workflow).toContain("--generate-notes") expect(workflow).toContain("--latest") + expect(workflow).toContain('--title "Atet $GITHUB_REF_NAME"') expect(workflow).toContain( "--json assets,isDraft,isImmutable,isPrerelease,tagName", ) @@ -122,10 +123,11 @@ test("version tags pass the complete immutable release gate", async () => { expect(workflow).not.toContain("administration:") }) -test("public vectorizer workflow verifies every reviewed platform without write permissions", async () => { +test("public Atet VTracer workflow verifies every reviewed platform without write permissions", async () => { const workflow = await readWorkflow("public-vectorizer.yml", "vectorizer.yml") expect(workflow).toContain("permissions:\n contents: read") + expect(workflow).toContain("name: Atet VTracer") expect(workflow).toContain("pull_request:") expect(workflow).toContain("branches: [main]") for (const target of [ diff --git a/scripts/validate-schema.ts b/scripts/validate-schema.ts index 898fd63..754c66a 100644 --- a/scripts/validate-schema.ts +++ b/scripts/validate-schema.ts @@ -27,6 +27,10 @@ const parsedSchema: unknown = JSON.parse(await readFile(schemaPath, "utf8")) if (!isRecord(parsedSchema)) { throw new Error("schema/diagram.schema.json must contain a JSON object.") } +const schemaId = "https://raw.githubusercontent.com/hraness/atet/v2.0.0/schema/diagram.schema.json" +if (parsedSchema.$id !== schemaId) { + throw new Error(`Diagram schema $id must be ${schemaId}.`) +} const ajv = new Ajv2020({ allErrors: true, @@ -45,6 +49,9 @@ for (const relativePath of [ const instance: unknown = JSON.parse( await readFile(join(repository, relativePath), "utf8"), ) + if (!isRecord(instance) || instance.$schema !== schemaId) { + throw new Error(`${relativePath} must reference the Atet v2.0.0 schema.`) + } if (!validate(instance)) { throw new Error( `${relativePath} does not satisfy the public schema:\n${formatErrors(validate.errors)}`, diff --git a/scripts/validate-skill.ts b/scripts/validate-skill.ts index b908281..58f8030 100644 --- a/scripts/validate-skill.ts +++ b/scripts/validate-skill.ts @@ -1,7 +1,7 @@ import { access, readFile } from "node:fs/promises" import { join } from "node:path" -const root = join(process.cwd(), "skills", "transmute") +const root = join(process.cwd(), "skills", "atet") const skillPath = join(root, "SKILL.md") const text = await readFile(skillPath, "utf8") const match = /^---\n([\s\S]*?)\n---\n/.exec(text) @@ -21,7 +21,7 @@ const keys = Object.keys(frontmatter).sort() if (keys.join(",") !== "description,name") { throw new Error(`SKILL.md frontmatter must contain only name and description, received ${keys}`) } -if (frontmatter.name !== "transmute") throw new Error("Skill name must be transmute") +if (frontmatter.name !== "atet") throw new Error("Skill name must be atet") if ((frontmatter.description?.length ?? 0) < 40) { throw new Error("Skill description must explain capability and triggers") } @@ -44,4 +44,4 @@ const openai = await readFile(join(root, "agents", "openai.yaml"), "utf8") for (const required of ["display_name:", "short_description:", "default_prompt:"]) { if (!openai.includes(required)) throw new Error(`agents/openai.yaml is missing ${required}`) } -console.log("transmute skill is valid") +console.log("atet skill is valid") diff --git a/skills/transmute/SKILL.md b/skills/atet/SKILL.md similarity index 87% rename from skills/transmute/SKILL.md rename to skills/atet/SKILL.md index be68add..3bea4cc 100644 --- a/skills/transmute/SKILL.md +++ b/skills/atet/SKILL.md @@ -1,11 +1,11 @@ --- -name: transmute -description: Create or update concise diagrams from a literal user prompt, keep checked source, generate light/dark exports, provide editable tldraw interchange, convert caller-owned raster artwork to bounded SVG, generate images directly through Vercel AI Gateway, or guide reviewed reference-image workflows for Three.js scenes and metallic logo treatments. Use for diagrams, flowcharts, maps, visual explanations, `.tldr` files, image-to-SVG conversion, image generation, low-poly or transparent 3D motion, metallic brand-symbol renders, semantic Transmute operations, or typed Bun workflows. +name: atet +description: Create or update concise diagrams from a literal user prompt, keep checked source, generate light/dark exports, provide editable tldraw interchange, convert caller-owned raster artwork to bounded SVG, generate images directly through Vercel AI Gateway, or guide reviewed reference-image workflows for Three.js scenes and metallic logo treatments. Use for diagrams, flowcharts, maps, visual explanations, `.tldr` files, image-to-SVG conversion, image generation, low-poly or transparent 3D motion, metallic brand-symbol renders, semantic Atet operations, or typed Bun workflows. --- # Create clear diagrams -Use the installed `transmute` CLI as the deterministic adapter. Keep the authored +Use the installed `atet` CLI as the deterministic adapter. Keep the authored `.diagram.json` source; treat `.light.svg`, `.dark.svg`, `.light.png`, `.dark.png`, and `.tldr` as replaceable exports. @@ -25,7 +25,7 @@ Treat the user's prompt as the complete content specification. ## Find the source -1. Read local repository instructions and look for `transmute.config.*`. +1. Read local repository instructions and look for `atet.config.*`. 2. Search for an existing same-subject `.diagram.json` before creating one. 3. Update that source rather than editing generated images or creating a duplicate. @@ -89,11 +89,11 @@ Use a coordinate-free `stack` layout for one horizontal or vertical sequence: ## Author and render -Use the public schema URL or run `transmute diagram init` for a starter: +Use the public schema URL or run `atet diagram init` for a starter: ```sh -transmute diagram check diagrams/.diagram.json --strict -transmute diagram render diagrams/.diagram.json +atet diagram check diagrams/.diagram.json --strict +atet diagram render diagrams/.diagram.json ``` The render command overwrites the consistent same-stem exports. Inspect both @@ -108,14 +108,14 @@ stack. ## Provide a Vercel AI Gateway credential Prefer a process-local environment variable. Never place a key on argv, write -it into a project file, print it, or ask Transmute to persist it: +it into a project file, print it, or ask Atet to persist it: ```sh export AI_GATEWAY_API_KEY='' ``` When the workspace is linked and the user is authenticated with Vercel, prefer -`vercel env run -- `. Transmute reads `AI_GATEWAY_API_KEY` first and +`vercel env run -- `. Atet reads `AI_GATEWAY_API_KEY` first and `VERCEL_OIDC_TOKEN` second. It has no product account, OAuth, database, or local credential store. @@ -125,7 +125,7 @@ When the user explicitly asks for raster generation, preserve their prompt rather than enriching it: ```sh -vercel env run -- transmute image generate \ +vercel env run -- atet image generate \ '' --output path/to/image.webp ``` @@ -167,7 +167,7 @@ When the user asks to convert caller-owned raster artwork to SVG, keep the raster as the source and treat the SVG as a replaceable derivative: ```sh -transmute image vectorize path/to/input.png --output path/to/input.svg --json +atet image vectorize path/to/input.png --output path/to/input.svg --json ``` - Do not provide a credential for vectorization. The conversion is local and @@ -183,7 +183,7 @@ transmute image vectorize path/to/input.png --output path/to/input.svg --json that an unfamiliar symbol communicates the intended concept. VTracer downloads from its checksum-pinned official release on first use. Use -`TRANSMUTE_VTRACER_PATH` only for a compatible local 0.6.4 binary; Transmute still +`ATET_VTRACER_PATH` only for a compatible local 0.6.4 binary; Atet still records its hash. Never add an upscaling model, embedded raster fallback, or commercial font to make a trace pass. @@ -193,9 +193,9 @@ unbounded temporary output file. ## Use a connected tool server narrowly -When Transmute MCP is connected, preserve the dedicated `check_diagram` and -`render_diagram` tools for simple compatibility calls. Use `search_transmute` -to discover the fixed semantic registry and `execute_transmute` with one exact +When Atet MCP is connected, preserve the dedicated `check_diagram` and +`render_diagram` tools for simple compatibility calls. Use `search_atet` +to discover the fixed semantic registry and `execute_atet` with one exact returned code and typed JSON. Continue to edit checked diagram source directly; no tool creates or rewrites source. @@ -208,9 +208,9 @@ no tool creates or rewrites source. - MCP mode deliberately uses built-in themes and icons and never executes workspace config. Use the CLI outside MCP when a trusted local config is required. -- `transmute.image.vectorize` accepts root-relative input/output paths, requires +- `atet.image.vectorize` accepts root-relative input/output paths, requires no login, and keeps raster bytes local. -- `transmute.image.generate` requires a root-relative `.webp` output path, +- `atet.image.generate` requires a root-relative `.webp` output path, atomically writes the bounded image, and returns only redacted file/request metadata. Use only the two registry model IDs and do not retry it. - Never pass source code, shell text, dynamic-import text, remote URLs, or @@ -219,8 +219,8 @@ no tool creates or rewrites source. Without MCP, the equivalent machine-readable CLI surface is: ```sh -transmute code search '' --limit 4 -transmute code execute --input '' +atet code search '' --limit 4 +atet code execute --input '' ``` ## Use tldraw deliberately @@ -230,10 +230,10 @@ tldraw SDK or desktop app to create. Open it in tldraw Offline when a person wants direct canvas editing: ```sh -transmute canvas open diagrams/.tldr +atet canvas open diagrams/.tldr ``` -If the optional app is absent, `transmute canvas install` resolves the current +If the optional app is absent, `atet canvas install` resolves the current official release, verifies its published SHA-256 digest, and launches the platform installer. The app imports `.tldr` as an unsaved document; save it there to create its newer native `.tldraw` bundle. Never rewrite a native @@ -241,8 +241,8 @@ there to create its newer native `.tldraw` bundle. Never rewrite a native ## Verify -1. Run `transmute diagram check --strict`. -2. Run `transmute diagram render `. +1. Run `atet diagram check --strict`. +2. Run `atet diagram render `. 3. Confirm all five artifacts exist beside the source or in the requested output directory. 4. Inspect light and dark output at actual size. diff --git a/skills/transmute/agents/openai.yaml b/skills/atet/agents/openai.yaml similarity index 69% rename from skills/transmute/agents/openai.yaml rename to skills/atet/agents/openai.yaml index b2517be..d5e3bb9 100644 --- a/skills/transmute/agents/openai.yaml +++ b/skills/atet/agents/openai.yaml @@ -1,4 +1,4 @@ interface: - display_name: "Transmute" + display_name: "Atet" short_description: "Create checked visuals, images, and reviewed 3D scenes" - default_prompt: "Create or update a visual that follows my prompt literally, keeps checked source and exact references, and uses reviewed Transmute workflows with bounded rendering defaults." + default_prompt: "Create or update a visual that follows my prompt literally, keeps checked source and exact references, and uses reviewed Atet workflows with bounded rendering defaults." diff --git a/skills/transmute/references/customization.md b/skills/atet/references/customization.md similarity index 86% rename from skills/transmute/references/customization.md rename to skills/atet/references/customization.md index 1d2e63b..acdc238 100644 --- a/skills/transmute/references/customization.md +++ b/skills/atet/references/customization.md @@ -1,13 +1,13 @@ # Fonts and icon adapters -`transmute` deliberately ships no commercial font and no large icon set. +`atet` deliberately ships no commercial font and no large icon set. ## Custom font -Create `transmute.config.ts` beside the source: +Create `atet.config.ts` beside the source: ```ts -import type { DiagramConfig } from "@hraness/transmute" +import type { DiagramConfig } from "@hraness/atet" export default { font: { @@ -42,7 +42,7 @@ the export adapter owns custom typography. An icon definition has a view box and SVG body: ```ts -import type { DiagramConfig } from "@hraness/transmute" +import type { DiagramConfig } from "@hraness/atet" export default { icons: { @@ -64,7 +64,7 @@ separate movable tldraw shapes. To use a third-party icon package, write a small local adapter that converts the package's data into `{ viewBox, body }`. Keep that package in the consuming -repository rather than adding it to `transmute`. Preserve the icon package's +repository rather than adding it to `atet`. Preserve the icon package's license and attribution requirements. Icon bodies may contain ordinary SVG geometry such as `path`, `circle`, `rect`, diff --git a/skills/transmute/references/reference-led-3d.md b/skills/atet/references/reference-led-3d.md similarity index 94% rename from skills/transmute/references/reference-led-3d.md rename to skills/atet/references/reference-led-3d.md index f31ae52..cc6414f 100644 --- a/skills/transmute/references/reference-led-3d.md +++ b/skills/atet/references/reference-led-3d.md @@ -1,8 +1,8 @@ # Reference-led Three.js and metallic treatments -Use this guide only when Transmute's complete local Code Mode host is available. +Use this guide only when Atet's complete local Code Mode host is available. It ships in this repository alongside the thin desktop capture shell. The -portable `@hraness/transmute/code` and `@hraness/transmute/workflow` +portable `@hraness/atet/code` and `@hraness/atet/workflow` entrypoints intentionally expose no HTML renderer, Three.js operation, or arbitrary source execution. Those capabilities belong to the local host. @@ -15,7 +15,7 @@ authority. record. 2. Inspect the subject, silhouette, palette, proportions, negative space, and intended polygon character. -3. Run `transmute html scaffold three --output `. The command does +3. Run `atet html scaffold three --output `. The command does not overwrite an existing source. 4. Edit only the scaffold's `createSubject()` region unless the requested camera, lights, or timing require a reviewed change elsewhere. @@ -61,12 +61,12 @@ private URL and settle decoding before frame zero: ```js const texture = new THREE.TextureLoader() - .loadAsync(TransmuteOverlay.asset("reference-image")) + .loadAsync(AtetOverlay.asset("reference-image")) .then((loaded) => { loaded.colorSpace = THREE.SRGBColorSpace; return loaded; }); -TransmuteOverlay.ready(texture); +AtetOverlay.ready(texture); ``` Never load a remote URL, data URL, object URL, undeclared font, environment map, @@ -75,7 +75,7 @@ or model file from scene code. ## Preserve deterministic motion - Derive every visible state from `progress`, `timeMs`, parameters, or - `TransmuteOverlay.randomFor(key)`. + `AtetOverlay.randomFor(key)`. - Do not use `requestAnimationFrame`, `setAnimationLoop`, wall-clock time, mutable random sequences, pointer input, or cumulative per-frame state. - Create geometry, materials, textures, vectors, matrices, and arrays before diff --git a/skills/transmute/references/visual-communication.md b/skills/atet/references/visual-communication.md similarity index 99% rename from skills/transmute/references/visual-communication.md rename to skills/atet/references/visual-communication.md index b7beddb..5a82982 100644 --- a/skills/transmute/references/visual-communication.md +++ b/skills/atet/references/visual-communication.md @@ -1,4 +1,4 @@ -# Visual communication rules +# Atet visual communication rules Use these rules to turn supplied content into a diagram without adding content. diff --git a/tsconfig.json b/tsconfig.json index bede562..9d55449 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,43 +9,43 @@ "moduleResolution": "Bundler", "moduleDetection": "force", "paths": { - "@hraness/transmute": [ + "@hraness/atet": [ "./src/index.ts" ], - "@hraness/transmute/cli": [ + "@hraness/atet/cli": [ "./src/cli.ts" ], - "@hraness/transmute/code": [ + "@hraness/atet/code": [ "./src/code/index.ts" ], - "@hraness/transmute/code/advanced": [ + "@hraness/atet/code/advanced": [ "./src/code/advanced.ts" ], - "@hraness/transmute/generate": [ + "@hraness/atet/generate": [ "./src/generate.ts" ], - "@hraness/transmute/host-resources": [ + "@hraness/atet/host-resources": [ "./src/host-resources.ts" ], - "@hraness/transmute/operations": [ + "@hraness/atet/operations": [ "./src/operations.ts" ], - "@hraness/transmute/workflow": [ + "@hraness/atet/workflow": [ "./src/workflow.ts" ], - "@hraness/transmute/scene": [ + "@hraness/atet/scene": [ "./packages/scene/src/index.ts" ], - "@hraness/transmute/local/code": [ + "@hraness/atet/local/code": [ "./apps/desktop/code/public.ts" ], - "@hraness/transmute/local/code/advanced": [ + "@hraness/atet/local/code/advanced": [ "./apps/desktop/code/advanced.ts" ], - "@hraness/transmute/local/code/workflows": [ + "@hraness/atet/local/code/workflows": [ "./apps/desktop/workflows/index.ts" ], - "@hraness/transmute/local/html-overlay": [ + "@hraness/atet/local/html-overlay": [ "./apps/desktop/html-overlay/index.ts" ] }, From 206b0eaf77f594df34fa4f49db6a56bf0630787d Mon Sep 17 00:00:00 2001 From: 0thernet Date: Sat, 15 Aug 2026 05:20:04 -0400 Subject: [PATCH 02/10] feat(web): launch atet.sh --- apps/web/AGENTS.md | 14 +- apps/web/README.md | 18 +- apps/web/package.json | 4 +- apps/web/scripts/build.ts | 88 ++- apps/web/site.test.ts | 259 +++++++- apps/web/src/404.html | 21 +- apps/web/src/apple-touch-icon.png | Bin 0 -> 9131 bytes apps/web/src/icon.svg | 18 +- apps/web/src/index.html | 357 ++++++---- apps/web/src/og.png | Bin 0 -> 958796 bytes apps/web/src/robots.txt | 2 +- apps/web/src/sitemap.xml | 5 +- apps/web/src/styles.css | 1017 +++++++++++++++++++++++++---- apps/web/src/theme.js | 2 +- apps/web/vercel.json | 55 ++ 15 files changed, 1541 insertions(+), 319 deletions(-) create mode 100644 apps/web/src/apple-touch-icon.png create mode 100644 apps/web/src/og.png diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index 00b7bad..1a36adb 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -1,15 +1,17 @@ # Contents -- `src/` contains the complete static `transmute.rocks` document, styles, appearance control, crawler files, and icon. -- `scripts/build.ts` copies the explicit public asset set into `dist/` without bundling or dependency installation. -- `site.test.ts`, `package.json`, and `vercel.json` define the content, performance, and deployment contract. +- `src/` contains the complete static `atet.sh` document, visual system, appearance control, crawler files, favicons, and social preview. +- `scripts/build.ts` renders fingerprinted local assets into `dist/` from an explicit allowlist without dependencies or network access. +- `site.test.ts`, `package.json`, and `vercel.json` define the content, identity, accessibility, performance, legacy-host, and deployment contracts. # Guidelines -- Keep the site static and usable without JavaScript. JavaScript may improve appearance controls, but it must not call a network API or receive credentials. -- Describe the released SDK, local host, and desktop capture shell as one Transmute system. Do not introduce a hosted account, billing, authentication, or generation service. +- Keep the site static and useful without JavaScript. JavaScript may improve appearance controls, but it must never call a network API, load remote code, or receive credentials. +- Describe the released SDK, local host, and desktop capture shell as one Atet system. Do not introduce a hosted account, billing, authentication, or generation service. - Keep generation credentials in local SDK or CLI processes. The browser must never accept, store, forward, or render an AI Gateway credential. - Preserve the four public output families: images, diagrams, animated loops, and video. Treat audio and captions as composable project inputs rather than another project model. -- Keep the homepage concise, semantic, keyboard-operable, readable at 200% zoom, and free of analytics, remote fonts, and client frameworks. +- Use Atet, Ra's solar barque, respectfully as an abstract metaphor for passage and transformation. Do not imitate sacred figures, hieroglyphs, or archaeological objects. +- Keep the homepage concise, semantic, keyboard-operable, readable at 200% zoom, and free of analytics, remote fonts, client frameworks, and runtime network requests. - Use the canonical Hraness footer lockup. Keep only the homepage in crawler discovery until another durable public route exists. +- Preserve permanent production and preview redirects for every reviewed predecessor host without redirecting canonical Atet hosts. - Run `bun run check` in this directory after a site change. diff --git a/apps/web/README.md b/apps/web/README.md index 6b78ca4..045ab7f 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,13 +1,17 @@ -# Transmute web +# Atet web -`transmute.rocks` is a dependency-free static product note. It has no server, -account surface, API route, analytics, or browser credential path. Generation -runs in the local Transmute SDK or CLI with a Vercel AI Gateway credential. +`atet.sh` is the dependency-free public site for Atet. It presents the SDK, +Bun CLI, local runtime, and desktop capture shell without adding a server, +account surface, API route, analytics, remote font, or browser credential path. +Generation runs from the local Atet SDK or CLI with the operator's Vercel AI +Gateway access. ```sh bun run check ``` -The build copies an explicit allowlist from `src/` into `dist/`. Configure the -Vercel project with this directory as its Root Directory. The checked -`vercel.json` performs no dependency install and serves only the built files. +The build fingerprints the local stylesheet and appearance script, then copies +an explicit allowlist from `src/` into `dist/`. Configure the Vercel project +with this directory as its Root Directory. The checked `vercel.json` performs +no dependency install, serves only built files under a strict CSP, and sends +reviewed predecessor hosts to their matching Atet production or preview host. diff --git a/apps/web/package.json b/apps/web/package.json index c6cae11..c24a291 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,8 +1,8 @@ { - "name": "@hraness/transmute-web", + "name": "@hraness/atet-web", "version": "0.0.0", "private": true, - "description": "The static public site for Transmute.", + "description": "The dependency-free static public site for Atet.", "type": "module", "packageManager": "bun@1.3.14", "scripts": { diff --git a/apps/web/scripts/build.ts b/apps/web/scripts/build.ts index fda1e96..8373061 100644 --- a/apps/web/scripts/build.ts +++ b/apps/web/scripts/build.ts @@ -1,4 +1,5 @@ -import { cp, mkdir, rm, stat } from "node:fs/promises" +import { createHash } from "node:crypto" +import { cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises" import { basename, dirname, join } from "node:path" import { fileURLToPath } from "node:url" @@ -6,29 +7,80 @@ const appDirectory = dirname(dirname(fileURLToPath(import.meta.url))) const sourceDirectory = join(appDirectory, "src") const outputDirectory = join(appDirectory, "dist") -const publicFiles = [ - "404.html", +const copiedFiles = [ + "apple-touch-icon.png", "icon.svg", - "index.html", + "og.png", "robots.txt", "sitemap.xml", - "styles.css", - "theme.js", ] as const -await rm(outputDirectory, { force: true, recursive: true }) -await mkdir(outputDirectory, { recursive: true }) +function assetPath(name: string, bytes: Uint8Array): string { + const digest = createHash("sha256").update(bytes).digest("hex").slice(0, 12) + const extensionIndex = name.lastIndexOf(".") + const stem = name.slice(0, extensionIndex) + const extension = name.slice(extensionIndex) + return `/assets/${stem}-${digest}${extension}` +} -for (const file of publicFiles) { - const source = join(sourceDirectory, file) - const sourceStat = await stat(source) - if (!sourceStat.isFile()) { - throw new Error(`Static source is not a regular file: ${basename(source)}`) +function renderDocument(template: string, assets: Readonly>): string { + let rendered = template + for (const [placeholder, value] of Object.entries(assets)) { + if (!rendered.includes(placeholder)) { + throw new Error(`Static document is missing ${placeholder}`) + } + rendered = rendered.replaceAll(placeholder, value) + } + if (/\{\{[A-Z0-9_]+\}\}/u.test(rendered)) { + throw new Error("Static document contains an unresolved placeholder") } - await cp(source, join(outputDirectory, file), { - dereference: true, - errorOnExist: true, - }) + return rendered } -console.log(`Built ${publicFiles.length} static files in ${outputDirectory}`) +export async function buildWebsite(): Promise> { + const [indexTemplate, notFoundTemplate, styles, theme] = await Promise.all([ + readFile(join(sourceDirectory, "index.html"), "utf8"), + readFile(join(sourceDirectory, "404.html"), "utf8"), + readFile(join(sourceDirectory, "styles.css")), + readFile(join(sourceDirectory, "theme.js")), + ]) + + const stylesPath = assetPath("styles.css", styles) + const themePath = assetPath("theme.js", theme) + const assets = { + "{{CSS_ASSET}}": stylesPath, + "{{THEME_ASSET}}": themePath, + } as const + + await rm(outputDirectory, { force: true, recursive: true }) + await mkdir(join(outputDirectory, "assets"), { recursive: true }) + + await Promise.all([ + writeFile(join(outputDirectory, "index.html"), renderDocument(indexTemplate, assets)), + writeFile(join(outputDirectory, "404.html"), renderDocument(notFoundTemplate, assets)), + writeFile(join(outputDirectory, stylesPath.slice(1)), styles), + writeFile(join(outputDirectory, themePath.slice(1)), theme), + ]) + + for (const file of copiedFiles) { + const source = join(sourceDirectory, file) + const sourceStat = await stat(source) + if (!sourceStat.isFile()) { + throw new Error(`Static source is not a regular file: ${basename(source)}`) + } + await cp(source, join(outputDirectory, file), { + dereference: true, + errorOnExist: true, + }) + } + + return { stylesPath, themePath } +} + +if (import.meta.main) { + await buildWebsite() + console.log(`Built ${copiedFiles.length + 4} static files in ${outputDirectory}`) +} diff --git a/apps/web/site.test.ts b/apps/web/site.test.ts index 3d889b4..354a049 100644 --- a/apps/web/site.test.ts +++ b/apps/web/site.test.ts @@ -1,81 +1,294 @@ -import { describe, expect, test } from "bun:test" -import { readFile } from "node:fs/promises" +import { beforeAll, describe, expect, test } from "bun:test" +import { readFile, readdir } from "node:fs/promises" import { dirname, join } from "node:path" import { fileURLToPath } from "node:url" +import { buildWebsite } from "./scripts/build" + const appDirectory = dirname(fileURLToPath(import.meta.url)) +const description = "Open-source TypeScript SDK, Bun CLI, and local runtime for turning ideas and raw assets into images, diagrams, animated loops, and video." +let builtAssets: Awaited> + +beforeAll(async () => { + builtAssets = await buildWebsite() +}) async function readSource(path: string): Promise { return await readFile(join(appDirectory, "src", path), "utf8") } -describe("static Transmute site", () => { +describe("static Atet site", () => { + test("publishes one canonical Atet identity across discovery metadata", async () => { + const html = await readSource("index.html") + + expect(html).toContain("Atet: code-first visual media from source to final") + expect(html).toContain(``) + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).toContain('') + expect(html).not.toMatch(/Transmute|transmute\.rocks|hraness\.(?:graphics|studio)/) + }) + + test("links the website, product, and source in structured data", async () => { + const html = await readSource("index.html") + const match = /`) + expect(notFound).toContain(``) + expect(notFound).toContain(``) + expect(`${html}\n${notFound}`).not.toContain("{{") + expect(rootFiles.sort()).toEqual([ + "404.html", + "apple-touch-icon.png", + "assets", + "icon.svg", + "index.html", + "og.png", + "robots.txt", + "sitemap.xml", + ]) + expect(assetFiles.sort()).toEqual([ + builtAssets.stylesPath.split("/").at(-1), + builtAssets.themePath.split("/").at(-1), + ].sort()) }) - test("publishes only the homepage to crawler discovery", async () => { + test("publishes only the canonical homepage to crawler discovery", async () => { + const robots = await readSource("robots.txt") const sitemap = await readSource("sitemap.xml") + const notFound = await readSource("404.html") const locations = [...sitemap.matchAll(/([^<]+)<\/loc>/g)] .map(match => match[1]) - expect(locations).toEqual(["https://transmute.rocks/"]) + expect(robots).toBe("User-agent: *\nAllow: /\n\nSitemap: https://atet.sh/sitemap.xml\n") + expect(locations).toEqual(["https://atet.sh/"]) + expect(notFound).toContain('') + }) + + test("redirects each reviewed predecessor host without looping Atet", async () => { + const vercel = JSON.parse( + await readFile(join(appDirectory, "vercel.json"), "utf8"), + ) as { + redirects?: Array<{ + source?: string + has?: Array<{ type?: string; value?: string }> + destination?: string + permanent?: boolean + }> + } + const redirects = vercel.redirects ?? [] + const projected = redirects.map(redirect => ({ + source: redirect.source, + host: redirect.has?.[0], + destination: redirect.destination, + permanent: redirect.permanent, + })) + + expect(projected).toEqual([ + { source: "/:path*", host: { type: "host", value: "transmute.rocks" }, destination: "https://atet.sh/:path*", permanent: true }, + { source: "/:path*", host: { type: "host", value: "hraness.graphics" }, destination: "https://atet.sh/:path*", permanent: true }, + { source: "/:path*", host: { type: "host", value: "hraness.studio" }, destination: "https://atet.sh/:path*", permanent: true }, + { source: "/:path*", host: { type: "host", value: "preview.transmute.rocks" }, destination: "https://preview.atet.sh/:path*", permanent: true }, + { source: "/:path*", host: { type: "host", value: "preview.hraness.graphics" }, destination: "https://preview.atet.sh/:path*", permanent: true }, + { source: "/:path*", host: { type: "host", value: "preview.hraness.studio" }, destination: "https://preview.atet.sh/:path*", permanent: true }, + ]) + + for (const redirect of projected) { + const sourceHost = redirect.host?.value + expect(sourceHost).not.toBe("atet.sh") + expect(sourceHost).not.toBe("preview.atet.sh") + expect(new URL(redirect.destination?.replace(":path*", "") ?? "https://invalid").host) + .not.toBe(sourceHost) + } + }) + + test("serves a strict CSP, security headers, and immutable fingerprinted assets", async () => { + const vercel = JSON.parse( + await readFile(join(appDirectory, "vercel.json"), "utf8"), + ) as { + headers?: Array<{ source?: string; headers?: Array<{ key?: string; value?: string }> }> + } + const global = vercel.headers?.find(entry => entry.source === "/(.*)")?.headers ?? [] + const assets = vercel.headers?.find(entry => entry.source === "/assets/(.*)")?.headers ?? [] + const byKey = new Map(global.map(header => [header.key, header.value])) + const csp = byKey.get("Content-Security-Policy") ?? "" + + expect(csp).toContain("connect-src 'none'") + expect(csp).toContain("font-src 'none'") + expect(csp).toContain("form-action 'none'") + expect(csp).toContain("frame-ancestors 'none'") + expect(csp).toContain("object-src 'none'") + expect(byKey.get("Strict-Transport-Security")).toContain("includeSubDomains") + expect(assets).toContainEqual({ + key: "Cache-Control", + value: "public, max-age=31536000, immutable", + }) + }) + + test("preserves the canonical Hraness footer", async () => { + const html = await readSource("index.html") + + expect(html).toContain('href="https://hraness.com"') + expect(html).toContain('aria-label="hraness"') + expect(html).toContain("class=\"hraness-mark\"") + expect(html).toContain("Atet 1.0.0 · MIT · Local-first visual media.") }) }) diff --git a/apps/web/src/404.html b/apps/web/src/404.html index d088574..31b7c90 100644 --- a/apps/web/src/404.html +++ b/apps/web/src/404.html @@ -4,21 +4,22 @@ - + - - - Not found — Transmute + + + + Not found · Atet -
- +
+

404 · Route not found

+

This passage ends here.

+

The address may have changed. Atet and its current installation guide remain at the canonical home.

+ Return to atet.sh