@odla-ai/brand 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @odla-ai/brand
2
+
3
+ A conversational brand-book builder for odla apps — document and image uploads onto [odla-db](https://odla.ai/docs/packages/db) file storage, agent-driven color exploration with human-gated palette proposals, a zero-dependency OKLCH/WCAG color engine, and a token compiler that turns an approved brand book into [@odla-ai/ui](https://odla.ai/docs/packages/ui) design tokens. The conversation itself rides [@odla-ai/chat](https://odla.ai/docs/packages/chat).
4
+
5
+ ```sh
6
+ npm i @odla-ai/brand
7
+ ```
8
+
9
+ > **Agentic experiment.** This package is built and maintained by AI agents working from bounded runbooks with human review. Review its documented guarantees before relying on it.
10
+
11
+ ## The shape
12
+
13
+ The package is a **schema + rules + an agent skill + a color engine + a token compiler + a route factory** — not a service you run.
14
+
15
+ - **Proposals are the only door to the palette.** The agent explores colors (deterministic OKLCH math: harmonies, ramps, ΔEOK dedupe, WCAG contrast) and writes every candidate as an open `brand_proposal`. A human accepts or rejects; `resolve_proposal` is instructed to fire only on explicit user confirmation. Accepted proposals become `brand_palette` rows with full provenance.
16
+ - **Vision happens in the model's own turn.** `view_asset` fetches uploaded bytes worker-side and returns them as image/PDF blocks *inside the tool result* — gated on the model's `toolResultBlocks` catalog capability (`supportsBrandVision`), with a pre-turn attachment fallback for models without it. No AI calls inside any tool handler; every handler is deterministic and testable.
17
+ - **Uploads are worker-mediated.** The odla-db browser client cannot write files, so `createBrandRoutes` proxies multipart uploads onto the app's `$files` storage (content-type allowlist, size cap, unguessable paths) and keeps `brand_asset` rows deny-all so they always match real storage.
18
+ - **Tokens compile to the @odla-ai/ui contract.** `compileBrandTokens` maps swatch roles onto `--ui-*` names — always emitting every required token plus the accent-composing derived set, so a scoped preview island never keeps stale root composites — and `renderTokensCss` emits theme-structured CSS (light, dark, invert). Dark is derived algorithmically and contrast re-tuned per token.
19
+ - **Zero runtime dependencies.** The db client is injected structurally (a real `@odla-ai/db` `AdminDb` satisfies `BrandDb`); `@odla-ai/ai` is a types-only optional peer.
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { createBrandRoutes, BRAND_SCHEMA, brandRules } from "@odla-ai/brand";
25
+ import { init } from "@odla-ai/db";
26
+
27
+ const db = init({ appId, adminToken, endpoint });
28
+
29
+ // One-time provisioning: POST BRAND_SCHEMA to /schema and brandRules() to
30
+ // /admin/rules (or use the brandIntegration descriptor with the odla CLI).
31
+
32
+ const routes = createBrandRoutes({
33
+ db,
34
+ authorize: async (req) => (await isMember(req)) ? { id: await userId(req), kind: "human" } : null,
35
+ });
36
+
37
+ export default {
38
+ async fetch(req: Request): Promise<Response> {
39
+ return (await routes(req)) ?? new Response("not found", { status: 404 });
40
+ },
41
+ };
42
+ ```
43
+
44
+ Wire the agent by attaching `brandSkill` to a persona (or let the chat-agent worker route a `brandBotTrigger` dispatch into `dispatchBrandTurn`):
45
+
46
+ ```ts
47
+ import { createBrandPersona, supportsBrandVision } from "@odla-ai/brand";
48
+
49
+ const spec = inference.catalog[model];
50
+ const persona = createBrandPersona({
51
+ model,
52
+ brand: {
53
+ db, bookId, fileBaseUrl,
54
+ self: { selfId: botId, kind: "bot" },
55
+ visionInToolResults: supportsBrandVision(spec),
56
+ },
57
+ });
58
+ const run = await runAgent(inference, persona, { input: userMessage });
59
+ ```
60
+
61
+ Compile anywhere — worker, script, or browser:
62
+
63
+ ```ts
64
+ import { compileBrandTokens, renderTokensCss } from "@odla-ai/brand";
65
+
66
+ const { light, dark, warnings } = compileBrandTokens({ swatches });
67
+ const css = renderTokensCss(light, { dark }); // :root / [data-theme="dark"] / media guard
68
+ ```