npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@odla-ai/cli

v0.57.8

Published

Agent-operable CLI for bounded session orientation, project runbooks, provisioning, System AI, security jobs, secret transfer, and smoke checks.

Readme

@odla-ai/cli

⚠️ Early access — pre-1.0. Agents work from bounded runbooks; humans approve credentials, production changes, releases, and merges. APIs and exact package availability can change. Review the documented guarantees and limitations; this software is MIT-licensed and provided without warranty.

Project-neutral provisioning CLI for odla apps. It creates and validates an odla.config.mjs, then uses that config to register an app, enable services, push odla-db schema/rules, configure hosted app AI (or explicit BYOK), configure Clerk auth, record deployment links, compose declared npm capability integrations, connect Google Calendar booking, provision o11y ingest credentials, and transfer local or deployed Worker secrets without printing them. It also gives platform admins a scoped System AI control surface and gives app owners a provider-key-free hosted security command.

Provisioning does not know about any specific app. App identity, environments, services, schema, rules, integrations, auth, AI mode/model, and links all come from config. Operator commands can also receive explicit platform/app/env context so remote agents are not forced to manufacture a project checkout.

Separate developer runtimes on one shared sandbox

Several developers can share one Stripe test account without sharing a webhook signing secret, pointing Stripe at whichever preview deployed last, or creating a separate database for every person. Keep the shared database in envs, then declare each provider runtime independently:

npx --yes @odla-ai/cli@latest environment reconcile --runtime cory

The command derives the exact Cloudflare account and Worker name from Wrangler, declares the desired callback, and reconciles exactly one Stripe endpoint tagged with the app lifetime, data environment, and runtime. It also binds ODLA_RUNTIME to the selected Worker. Stripe's returned whsec_… value moves directly into the shared environment vault as stripe_webhook_secret__<runtime> and is never printed. The shared data environment must already contain stripe_secret_key; development reconciliation refuses live-mode keys.

The Chapter defaults are /api/webhooks/stripe with invoice.paid, payment_intent.succeeded, charge.refunded, and customer.subscription.deleted. Override only non-secret delivery intent:

export default {
  app: { id: "my-app", name: "My app" },
  envs: ["dev"],
  services: ["db"],
  runtimes: {
    cory: {
      dataEnvironment: "dev",
      wranglerEnvironment: "dev",
      origin: "https://my-app-cory.example.com",
    },
    tori: {
      dataEnvironment: "dev",
      wranglerEnvironment: "dev",
      origin: "https://my-app-tori.example.com",
    },
  },
  stripe: {
    webhookPath: "/api/webhooks/stripe",
    enabledEvents: ["invoice.paid", "customer.subscription.deleted"],
  },
};

Both Workers continue to use ODLA_ENV=dev and the same my-app--dev database. Chapter adds the originating runtime to Stripe objects; each endpoint verifies its own secret, and sibling runtimes acknowledge without writing shared data.

Use environment list for declared identities, environment status --runtime cory --json for desired-versus-live drift and custody, environment rotate --runtime cory --yes to replace only Cory's endpoint/secret, and environment retire --runtime cory --yes to remove only Cory's managed resources. Production, unmanaged endpoints, the shared provider key, and sibling runtimes are outside that mutation boundary. Existing --env alice declarations remain supported as the legacy one-runtime/one-data-environment form.

Start with the runbooks, not this file

odla's operational procedures — releasing, backups, database work, security setup — are rows in a database, not documents in a package. They are edited the moment a step turns out to be wrong, with no release in between. So the answer this CLI fetches is current, and every written copy of it (including this README, and including your training data) is a snapshot of some earlier day.

Ask before you search the web or work from memory:

npx --yes @odla-ai/cli@latest runbook ask "how do I roll back a bad publish?"   # written, cited answer
npx --yes @odla-ai/cli@latest runbook search "restore a tenant"                 # the passages behind it
npx --yes @odla-ai/cli@latest runbook get release                               # the whole procedure
npx --yes @odla-ai/cli@latest runbook list                                      # what exists

Reading works for any signed-in developer and needs no odla.config.mjs — the advice is most useful before there is a project.

Chapter member-content authoring

The CLI drives the package-owned member-content contract without duplicating its validator. Supply a bounded tier-authority file containing only id, name, priceCents, and active; provider Price ids do not belong in authored content or CLI output.

odla-ai chapter member-content validate --file parent.json --tiers tiers.json --json
odla-ai chapter member-content compose --parent parent.json --chapter chapter.json --tiers tiers.json --json
odla-ai chapter member-content project --parent parent.json --chapter chapter.json --tiers tiers.json --tier community --json

project is the disclosure check: locked pages contain metadata and an eligible live-tier offer, but never their restricted bodyMarkdown. Use --admin for the exact admin preview. compose is an operator-local authoring operation and therefore emits the complete effective document supplied in the local files.

Delivery validates the parent document again, computes its canonical digest, signs the exact body, and calls the dedicated Chapter receiver. The shared secret is never accepted in argv:

MEMBER_CONTENT_SECRET='...' \
  odla-ai chapter member-content deliver \
  --file parent.json --tiers tiers.json \
  --url https://chapter.example --from-env MEMBER_CONTENT_SECRET --json

# Or keep the value out of the environment:
secret-producing-command | odla-ai chapter member-content deliver \
  --file parent.json --tiers tiers.json \
  --url https://chapter.example --stdin --json

These commands require the project’s current @odla-ai/chapter installation. Outside localhost, delivery refuses plain HTTP and credential-bearing URLs.

Chapter Builder Library

Builder Library commands use Chapter's strict private-reference contract. They accept exactly one Chapter, Built Not Found-only, or global scope per resource; there are no groups. Validation output does not echo private bodies.

odla-ai chapter builder-library validate --file library.json --source-type chapter --source oakland --json
odla-ai chapter builder-library manifest --file library.json --chapter oakland --json
NETWORK_SHARE_SECRET='...' odla-ai chapter builder-library deliver \
  --file library.json --url https://builtnotfoundcapital.com \
  --from-env NETWORK_SHARE_SECRET --json

manifest emits only published resources visible to the named Chapter. Brand assets remain canonical book/asset references; manifests never carry bytes, storage credentials, or persistent private URLs. Delivery reuses the existing Chapter network share secret for that Built Not Found/chapter edge; Builder Library does not require another credential. The value is accepted only from stdin or a named environment variable. When delivering a Built Not Found local document to a Chapter, pass --target and --environment; delivery projects global resources only.

Selective release planning and approval

Promotion plans are exact, read-only review artifacts. An agent supplies the validated source bundles, target snapshots, mappings, and provider-readiness evidence; Registry returns one immutable digest and target-set digest without changing any target:

npx --yes @odla-ai/cli@latest promotion plan --file promotion-plan.json --env dev --json
npx --yes @odla-ai/cli@latest promotion inspect <plan-id> --env dev --json
npx --yes @odla-ai/cli@latest promotion status <plan-id> --env dev --json

Studio shows the exact before/after values, exclusions, mappings, blockers, and provider evidence. A signed-in human approves one apply or rollback action. An enrolled device can then queue or resume that exact operation; a stable default idempotency key makes an interrupted retry return the same intent:

npx --yes @odla-ai/cli@latest promotion apply <plan-id> --approval <approval-id> --env dev --json
npx --yes @odla-ai/cli@latest promotion rollback <plan-id> --approval <approval-id> --env dev --json
npx --yes @odla-ai/cli@latest promotion next <plan-id> <operation-id> --env dev --json
npx --yes @odla-ai/cli@latest promotion record <plan-id> <operation-id> <step-id> --file receipt.json --env dev --json
npx --yes @odla-ai/cli@latest promotion cancel <plan-id> <operation-id> --env dev --json

Approval never floats to a newer plan. A changed source, mapping, readiness check, target lifetime, target set, action, digest, or expiry fails closed. A follower leases one target-local step with next, records exact evidence with record, and may safely reclaim an expired lease after a process exit. Content activation is always last. cancel stops work only before activation; afterward an approved rollback publishes a higher audited revision.

Operator context from any directory

Inside an existing configured checkout, a bare invocation is the compact, read-only home for agents:

npx --yes @odla-ai/cli@latest

It reports the CLI version; config, app, environment, and credential-cache provenance; bounded current and Ready PM windows when usable authority is already cached; and fully scoped next commands. It makes at most one bounded PM GET. It never starts authentication, opens a browser, prompts, adopts legacy files, mints a device session, or writes a cache. Missing, expired, rejected, offline, and unresolved states are explicit. Use odla-ai --help for the full command manual.

Before an agent reads PM, Discussions, o11y, runbooks, or identity from a fresh workspace, it can explain the selected context without authenticating:

npx --yes @odla-ai/cli@latest context show --platform https://odla.ai --app <appId> --env prod --json
npx --yes @odla-ai/cli@latest context save production --platform https://odla.ai --app <appId> --env prod
npx --yes @odla-ai/cli@latest context show --context production --json

For each value, an explicit flag wins over the matching ODLA_PLATFORM_URL, ODLA_APP_ID, or ODLA_ENV variable. An explicitly selected --context (or ODLA_CONTEXT) comes next, followed by a present odla.config.mjs; the platform defaults to https://odla.ai, and environment defaults remain command-specific. There is deliberately no ambient “current” context: a saved profile cannot silently retarget commands. A missing named context or explicitly named missing --config is always an error. context list inventories profiles, and context remove <name> --yes removes metadata without deleting caches or revoking grants. Revoke a grant in Studio when its authority must end. Set ODLA_CONTEXT_FILE to relocate the default private ~/.odla/contexts.json file.

For unattended reads, supply the ordinary revocable owner grant through ODLA_DEV_TOKEN. Without a project config or explicit token, the normal device flow uses the private ~/.odla/dev-token.json cache. Every named context instead uses isolated developer and scoped-token caches beneath ~/.odla/profiles/<name>/; set ODLA_DEV_TOKEN_FILE or ODLA_ADMIN_TOKEN_FILE to override them. Profiles store scope metadata only and reject unknown fields, so credentials cannot be added to contexts.json. A checked-in project continues to use its configured cache when no profile is selected. Each cache records its platform audience, and a token for another origin is never reused. context show reports provenance and cache state only; it never returns a credential or starts a device handshake. When a cached credential expires, the approving human can reuse the agent's exact handle suffix to bind the replacement credential to the same durable principal. The approval appends new revisions for the exact projects selected; it does not automatically revoke the older credential or renew an omitted project. The installed references/agent-identity.md runbook gives the full cross-worktree recovery procedure.

ODLA_DEV_TOKEN=... npx --yes @odla-ai/cli@latest pm handoff --app <appId>
ODLA_DEV_TOKEN=... npx --yes @odla-ai/cli@latest discuss list --app <appId> --json
ODLA_DEV_TOKEN=... npx --yes @odla-ai/cli@latest o11y status --app <appId> --env prod --json
ODLA_DEV_TOKEN=... npx --yes @odla-ai/cli@latest whoami --json
npx --yes @odla-ai/cli@latest platform status --context production --json
npx --yes @odla-ai/cli@latest pm handoff --context production --json
npx --yes @odla-ai/cli@latest runbook new deploy --context production --title Deploy --file deploy.md

Runbooks tell an agent how to operate; PM tells every agent what this project is trying to prove, what is active, what was decided, and what is broken. Once the app is registered, start each session with the read-only aligned-work intake, then read open bugs and recent decisions:

npx --yes @odla-ai/cli@latest pm project list --app <productId>
npx --yes @odla-ai/cli@latest pm project add --app <productId> --name "My project"
npx --yes @odla-ai/cli@latest pm project use <projectId>
npx --yes @odla-ai/cli@latest pm brief --app <appId> --project <projectId>
npx --yes @odla-ai/cli@latest pm next --app <appId> --project <projectId>
npx --yes @odla-ai/cli@latest pm handoff --app <appId>
npx --yes @odla-ai/cli@latest pm watch --app <appId> --entity task --jsonl
npx --yes @odla-ai/cli@latest pm bug list --app <appId> --status open
npx --yes @odla-ai/cli@latest pm bug list --platform-wide --status open
npx --yes @odla-ai/cli@latest pm bug get <uuid-or-unique-prefix> --platform-wide
npx --yes @odla-ai/cli@latest pm goal list --app <appId> --status open
npx --yes @odla-ai/cli@latest pm decision list --app <appId> --limit 20

pm brief is the bounded agent intake: exact totals, current-principal work, the top Ready candidates, only their context goals, and explicit expansion commands. pm next --brief and pm handoff --brief are aliases for that projection. pm handoff without --brief remains the authoritative full unresolved-state view: unmet goals, every non-done task, and every open or triaged bug. File bugs with actionable context (pm bug add ... --description "reproduction/evidence"); a title-only bug is rejected. When an accepted decision resolves or explicitly retains a bug, link it with pm bug done <id> --decision <decision-id> or pm bug set <id> --status triaged --decision <decision-id>.

Read the active task, linked goal, and comments before changing code. Without --app, list commands span every project the account co-owns, which is the right first view when coordinating several efforts. A platform administrator can approve the dedicated platform:pm:read capability used by pm <goal|task|decision|bug> list|get --platform-wide; it reads core records across current app lifetimes but cannot read comments/history or perform any PM mutation. get accepts a unique UUID prefix and refuses missing or ambiguous matches. Scope every write to the exact appId from that project's config. PM requires a registered app. Before the first provision, use a focused commit/checkpoint, then initialize PM and backfill that evidence immediately after registration. Never use a parallel status diary or put a secret in PM.

Before project-mutating work, match the request to an open goal. If it is not aligned, discuss that with the user before creating an outcome or implementing. Refine a Backlog task with --goal (or an accepted --alignment-decision), --description, and --acceptance, then mark it Ready and start it:

npx --yes @odla-ai/cli@latest pm task ready <id> --expected-revision <n>
npx --yes @odla-ai/cli@latest pm start --app <appId>

Ready is stored as todo for API compatibility. pm start claims the top-ranked Ready task (or the one you name) in a single call: it reads the current revision, compare-and-swaps against it server-side, and reloads once if the task moved, returning the task, its goal, and the acceptance criteria. It cannot mark work Ready, so an agent cannot authorize its own work. Claims are atomic and assign the authenticated principal; stale or concurrent claims fail. pm task claim <id> --expected-revision <n> remains for a caller that has already read the revision. Use pm task release for an explicit recovery handoff instead of leaving Doing as a vague status. The complete runbook is installed as references/pm-work-intake.md by odla-ai skill install.

pm watch is the durable wake-up path for goal/task/decision/bug changes and comments attached to those work items. Its first request creates an opaque checkpoint; persist later checkpoint JSONL records and deduplicate at-least-once events by eventId. Use --action comment.created for comment traffic. A Ready event or comment is not a claim: reload the task and goal, then claim the current revision. General Discussion topics and mentions remain on discuss watch until PM topics gain context-aware capability enforcement. Use pm task ref <id> (or the equivalent goal/decision/bug command) to print copy-ready structured markup for Discussion. The same markup may be pasted into pm <entity> comment <id> --body "…"; PM stores it as a structured reference, and pm <entity> comments <id> prints copy-ready markup instead of flattening the link back to a title. For ordinary chat and handoffs, pm task link <id> prints a normal Markdown link to the record's durable Studio route. Successful create and lifecycle commands print that title-first link directly. Lead with the PM type and linked title, explain the state or next action, and keep the id hidden in the URL; a bare id list is not a useful handoff.

When diagnosing a deployed app, agents can request one parseable observability snapshot instead of scraping Studio or composing collector routes themselves:

npx --yes @odla-ai/cli@latest o11y status --app <appId> --env prod --minutes 60 --json

Schema v9 keeps application RED, an applicationVersions breakdown of request/error/latency evidence by exact Cloudflare Worker version, reconciled live-sync load/freshness and subscription recompute/fanout/payload/commit-to-send performance, the exact latest protected commit-to-visible canary, collector ingest/scheduler trust, the live Cloudflare Worker read with per-isolate memory headroom, same-window request-total reconciliation, and 30-day-retained provider snapshot history with a seven-day maximum per read separate. For odla-db, providerCapacity adds redacted account-scoped Durable Object, D1, and R2 evidence: storage, query/operation/row volume, worst database-hour latency, socket/message load, objects/uploads, source ages, and per-dataset truncation. Other app scopes return an explicit not-applicable envelope without reading account data. The reconciliation ends five minutes behind real time and grades monitoring completeness without treating a coverage gap as application failure. Scheduled collection failures, source age, expected cadence, collection lag, and history truncation remain machine-readable. It derives one healthy, degraded, or unhealthy verdict with stable reason codes while retaining every source response for diagnosis. Provider unavailability, adaptive sampling, publication delay, collector loss, quota rejection, stale jobs, missing canary configuration, stale probes, and partial socket-reconciliation coverage remain explicit fields. Authentication progress is stderr-only, so stdout is a single JSON document.

Repository-controlled SLO monitoring lives under o11y.monitoring. Objectives can consume Kitesurf routes or bounded observations of existing o11y error, latency, and protected-canary signals. A minimal route policy detects both short outages and sustained error-budget burn, and sends incident and digest email:

export default {
  services: ["o11y"],
  links: { prod: "https://example.com" },
  o11y: {
    monitoring: {
      probes: [{
        id: "home", route: "/", envs: ["prod"], every: "5m",
        expect: { status: 200, titleIncludes: "Example", textIncludes: ["Welcome"] },
      }],
      slos: [{
        id: "availability", name: "Public availability",
        indicator: { type: "probe-success", probes: ["home"] },
        target: 0.999, window: "28d",
        alerts: {
          spike: { badChecks: 2, withinChecks: 3, recoverAfter: 2 },
          trend: { burnRate: 1, shortWindow: "6h", longWindow: "3d", minBadChecks: 2 },
        },
      }],
      notifications: {
        prod: {
          email: ["[email protected]"], timezone: "America/Costa_Rica",
          daily: "08:00", weekly: { day: "monday", at: "08:00" },
        },
      },
    },
  },
};

For a service-performance objective, omit probes and select an existing o11y metric. The comparator describes a successful observation, so this example requires each five-minute request p95 to be at most 500ms:

slos: [{
  id: "request-latency", name: "Request p95 at or below 500ms",
  indicator: {
    type: "o11y-metric", metric: "latency_p95", comparator: "lte",
    threshold: 500, every: "5m", observationWindow: "5m",
  },
  target: 0.99, window: "28d",
}]

error_rate, latency_p95, synthetic_success, and synthetic_publish_to_visible are supported. The checked-in odla-db first rollout is ops/monitoring/odla-db.config.mjs:

npx --yes @odla-ai/cli@latest monitor plan --config ops/monitoring/odla-db.config.mjs --env prod --json

Preview and apply the normalized policy, then inspect or exercise it through stable JSON:

npx --yes @odla-ai/cli@latest monitor plan --env prod --json
npx --yes @odla-ai/cli@latest monitor apply --env prod --yes --json
npx --yes @odla-ai/cli@latest monitor status --app <appId> --env prod --json
npx --yes @odla-ai/cli@latest monitor run home --app <appId> --env prod --json
npx --yes @odla-ai/cli@latest monitor incidents --app <appId> --env prod --runs --json
npx --yes @odla-ai/cli@latest monitor report --app <appId> --env prod --period weekly --json

Only scheduled good and bad outcomes enter the application SLO denominator. Kitesurf failures, missing Analytics Engine evidence, stale canaries, query failures, and scheduler gaps are retained as monitor_error/missed and degrade monitoring health separately, so no traffic or a broken monitor cannot manufacture success or downtime. Probe responses retain bounded metadata only; page content and accessibility trees are never returned by the CLI or stored. Each applied desired-state revision starts a fresh SLO event series and closes open incidents as config_revision_changed; results produced by different thresholds, routes, assertions, or notification policy are never blended.

For platform-wide operations, platform status --json requests only the short-lived, admin-approved platform:status:read capability and reads the same odla.platform-status/v1 snapshot as Studio. It works outside a project checkout, derives its complete six-Worker inventory from the deployment catalog, probes private services over bindings, and joins observed release identity with retained Cloudflare request/error/CPU/wall-time/memory evidence. Unavailable health, credentials, versions, or provider snapshots remain explicit rather than becoming zero; the document includes stable reason codes and exact next actions. The latest successful main deployment persists its normalized receipt in Registry D1 after exact-version probes pass. Fleet status therefore compares that verified expectation with the Worker answering now and reports matched, drifted, or unknown; an empty or unavailable receipt ledger degrades instead of silently treating observed health as intended state.

For repository-managed settings, compare checked-in intent with the live owner-visible Registry projection before provisioning:

npx --yes @odla-ai/cli@latest config diff --json
npx --yes @odla-ai/cli@latest config plan --json > .odla/config-plan.json
npx --yes @odla-ai/cli@latest config apply --plan .odla/config-plan.json --json
npx --yes @odla-ai/cli@latest operations wait <operation-id> --json

diff and plan are read-only and request only a short-lived, owner-approvable app:config:read capability. It opens only exact-id GET /registry/apps/<appId> reads for apps the approver owns; app listing, writes, PM, Discussions, directory access, data-plane delegation, and every other Registry route remain closed. diff emits separate SHA-256 revisions for desired and observed state, explicit in_sync, different, or unmanaged status, field provenance, and a bounded coverage declaration. Runtime auth or links omitted from project config stay visible as unmanaged rather than being silently overwritten.

plan v2 freezes those content revisions, the monotonic Registry registry:<number> revision, and ordered actions into a stable digest. Enable/configure actions are dependency-first; proposed disablements are dependent-first, high-risk, approval-required, and Studio-only.

config apply accepts only that saved JSON plan. Before requesting authority it recomputes the digest, verifies the selected app/platform, confirms checked-in intent has not changed, and rejects every production, destructive, approval-marked, high-risk, Studio, auth, creation, or disable action. The short-lived app:config:write grant reaches only this app's operation reserve/resume/read routes; it cannot enumerate apps or call direct mutation routes. A deterministic default idempotency key makes retrying the same plan resume the same durable journal entry, while the Registry CAS revision rejects stale state.

Apply prints the operation receipt, including ordered step attempts and its canonical environment-settings recovery URL. Studio preserves the exact operation id in that URL and renders the matching operations get <id> --json recovery command. Plans that require Studio now link to the explicit app/environment settings scope instead of an ambiguous app-level Settings page. operations get reads one exact id without replaying it; operations wait polls that id for 60 seconds by default (--timeout and --interval are explicit seconds). A still-running bounded wait exits 75 after printing its last receipt. Every terminal receipt is independently digest-verified by the CLI before success. Schema, rules, integration seeds, credentials, vault or Worker secrets, OAuth connection state, and opaque controller-assigned service fields remain outside this boundary.

Runbooks are procedure; JSDoc is API. What a function takes, returns, and guarantees is documented on the export itself, ships in the installed .d.ts, and renders per package at https://odla.ai/docs. A runbook will tell you when to call something and never invents a signature. Most real questions need both halves — take the steps from the runbook and the arguments from the JSDoc, and neither from memory.

After you change something, ask which runbooks you invalidated:

npx --yes @odla-ai/cli@latest runbook impact               # vs origin/main, uncommitted work included
npx --yes @odla-ai/cli@latest runbook impact --base HEAD~1

It diffs the working tree, works out which surfaces moved — a package, its exported API, a JSDoc block, a console area — and names the runbooks whose steps describe them, with the command to read each one. Changing an exported API or its JSDoc is exactly when a procedure quoting that API goes stale, so run it before you call the change finished. Fixing what it finds is one command and takes effect immediately:

npx --yes @odla-ai/cli@latest runbook edit db --note "retract replaced the null write"

Platform runbook edits need admin or the platform:runbook:write capability, which the CLI requests for you; a plain handshake token is never admin by design. odla-ai whoami reports the actual credential-bound principal by friendly name, handle, and kind; an agent's manager; the credential kind; and the accountable human owner separately. Manager metadata explains organization but never grants authority.

Is the corpus itself still true?

npx --yes @odla-ai/cli@latest runbook lint

Because a runbook is edited without a release, its text can name a command that only a newer CLI has — and no gate in any repository can see that, since the text is a row in a database rather than a file. lint closes that: it holds every odla-ai … command the runbooks name against this CLI's actual command surface, and against the minimum versions each runbook declares in requires.

A runbook may record the versions its steps assume, as name@version specs read as minimums (@odla-ai/[email protected]). When yours is older, runbook get says so on stderr — never stdout, so piping the procedure into an agent still yields the procedure and nothing else.

Install

No install needed — the package ships a single odla-ai binary, so npx can run it directly. Agents and one-off setup should resolve the current published CLI explicitly:

npx --yes @odla-ai/cli@latest init --app-id my-app --name "My App"

For a project you keep working in, install it as a dev dependency so the lockfile records the CLI/runtime graph used by that application. Use npx --no-install odla-ai only when deliberately testing that pinned graph; agent orchestration examples use @latest so a stale workspace or cache does not silently select an older CLI. ODLA is under active development: use a normal dependency declaration, commit the lockfile, and let npm ci reproduce the resolved graph:

npm view @odla-ai/cli version
npm i -D @odla-ai/cli
npm ls @odla-ai/cli

Prerequisites

  • Node.js 20 or newer (node --version).
  • An existing odla account that has signed in at least once. For any command that may need a fresh device grant, pass --email <account> or set ODLA_USER_EMAIL. This value identifies who may review the request; it is not a password or session credential, and an agent must never ask for either.
  • odla apps usually deploy as Cloudflare Workers. If you have never used Cloudflare:
    1. Create a free account at https://dash.cloudflare.com/sign-up.
    2. You do not need to install anything: npx wrangler login opens the browser once to link your account, npx wrangler dev runs a Worker locally, and npx wrangler deploy ships it.
    3. The .dev.vars file this CLI writes with provision --write-dev-vars is wrangler's local secrets file — wrangler dev picks it up automatically. It is chmod 0600 and gitignored; never commit it or paste its contents into wrangler.toml.

Commands

npx --yes @odla-ai/cli@latest auth login --app my-app --email [email protected] --no-open --wait 600
npx --yes @odla-ai/cli@latest runbook ask "how do I roll back a bad publish?"
npx --yes @odla-ai/cli@latest runbook search "restore a tenant" --limit 5
npx --yes @odla-ai/cli@latest runbook get release
npx --yes @odla-ai/cli@latest runbook list
npx --yes @odla-ai/cli@latest runbook impact --base origin/main
npx --yes @odla-ai/cli@latest runbook lint
npx --yes @odla-ai/cli@latest runbook edit db --note "what changed"
npx --yes @odla-ai/cli@latest runbook comment release --body "step 4 no longer applies"
npx --yes @odla-ai/cli@latest context show --app my-app --env prod --json
npx --yes @odla-ai/cli@latest whoami
npx --yes @odla-ai/cli@latest setup
npx --yes @odla-ai/cli@latest init --app-id my-app --name "My App"
npx --yes @odla-ai/cli@latest doctor
npx --yes @odla-ai/cli@latest ai models --env dev # exact models this app may select; no admin grant
npx --yes @odla-ai/cli@latest config diff --json
npx --yes @odla-ai/cli@latest config plan --json > .odla/config-plan.json
npx --yes @odla-ai/cli@latest config apply --plan .odla/config-plan.json --json
npx --yes @odla-ai/cli@latest operations get <operation-id> --json
npx --yes @odla-ai/cli@latest operations wait <operation-id> --timeout 60 --json
npx --yes @odla-ai/cli@latest calendar status --env dev
npx --yes @odla-ai/cli@latest calendar calendars --env dev
npx --yes @odla-ai/cli@latest calendar connect --env dev --no-open
npx --yes @odla-ai/cli@latest calendar disconnect --env dev --yes
npx --yes @odla-ai/cli@latest capabilities --json
npx --yes @odla-ai/cli@latest code connect --platform https://odla.ai --app-id my-app --env dev --email [email protected] --no-open
npx --yes @odla-ai/cli@latest code connect --env prod --once --no-open # bounded enrollment + heartbeat proof
# install SDKs, write the Worker, and create wrangler.jsonc before secret push
npx --yes @odla-ai/cli@latest provision --dry-run
npx --yes @odla-ai/cli@latest provision --email [email protected] --write-dev-vars --push-secrets --no-open --wait 600
# recovery when the selected agent credential has no live app.manage grant
npx --yes @odla-ai/cli@latest provision --request-grant --email [email protected] --write-dev-vars --push-secrets --no-open --wait 600
npx --yes @odla-ai/cli@latest smoke --env dev
npx --yes @odla-ai/cli@latest agent jobs --env dev --state dead_letter --json
npx --yes @odla-ai/cli@latest agent retry <job-id> --env dev --json
npx --yes @odla-ai/cli@latest discuss watch <topic-id> --jsonl
npx --yes @odla-ai/cli@latest discuss watch <topic-id> --cursor <saved-cursor> --jsonl
npx --yes @odla-ai/cli@latest secrets push --env dev
npx --yes @odla-ai/cli@latest secrets set clerk_webhook_secret --env dev --stdin
npx --yes @odla-ai/cli@latest secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY
npx --yes @odla-ai/cli@latest security run . --env dev --ack-redacted-source
npx --yes @odla-ai/cli@latest security github connect --env dev --no-open # infers owner/name from git origin
npx --yes @odla-ai/cli@latest security plan --env dev
npx --yes @odla-ai/cli@latest security sources --env dev
npx --yes @odla-ai/cli@latest security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
npx --yes @odla-ai/cli@latest security status <job-id>
npx --yes @odla-ai/cli@latest security report <job-id>
npx --yes @odla-ai/cli@latest admin ai show --context production --email [email protected] --no-open
npx --yes @odla-ai/cli@latest admin ai models --context production
npx --yes @odla-ai/cli@latest admin ai set security --context production --discovery-provider anthropic --discovery-model claude-opus-4-8 --validation-provider openai --validation-model gpt-5.5 --enabled
npx --yes @odla-ai/cli@latest admin ai credentials --context production
npx --yes @odla-ai/cli@latest admin ai credential set anthropic --context production --from-env ANTHROPIC_API_KEY
npx --yes @odla-ai/cli@latest admin ai usage --context production --app-id my-app --env prod --limit 50
npx --yes @odla-ai/cli@latest admin ai audit --context production --limit 25
npx --yes @odla-ai/cli@latest skill install
npx --yes @odla-ai/cli@latest setup --hooks install # opt-in bounded SessionStart context
npx --yes @odla-ai/cli@latest setup --hooks status
npx --yes @odla-ai/cli@latest setup --hooks remove
npx --yes @odla-ai/cli@latest version

Before a non-dry provisioning run, the executable verifies two independent things: its CLI version matches the current release, and every external @odla-ai/* runtime module resolves to the exact version pinned in that CLI's manifest. The command graph is imported only after both checks pass. A stale workspace-linked module is reported with its resolved package path and the agent is told to update/rebase the worktree, run npm ci, and rebuild. Released CLI dependencies are exact pins, so the printed npm exec --package=... recovery command installs the same dependency graph exercised by the release tarball test rather than reusing an arbitrary compatible older module.

agent jobs is the remote-operator view of commit-trigger delivery. It reads content-free lifecycle metadata—job id, trigger, entity reference, state, attempts, timing, and bounded error/status codes—using an audience-bound developer token for an owner of the selected environment. agent retry accepts one exact dead-lettered id and resets its attempt lease; it does not replay successful or currently running work. Successful receipts are retained for seven days (and bounded to the newest 1,000 per tenant); unresolved dead letters remain until an operator requeues them.

discuss watch waits at a server-issued snapshot boundary instead of comparing client clocks or repeatedly reading a topic's oldest 200 posts. It is unbounded unless --timeout <seconds> is supplied. --jsonl emits versioned checkpoint, event, heartbeat, and degraded status records; persist the latest cursor only after consuming its record and pass it back with --cursor after a restart. Event IDs are stable for at-least-once deduplication. Transient 429/5xx/network failures retry with bounded exponential backoff without advancing the cursor. A restore or truncated history exits 3 and emits an explicit replacement checkpoint; an explicit timeout exits 75, and an exhausted remote retry exits 6.

discuss read <topic> --json follows the server's bounded forward post pages before emitting one complete document. A long-lived agent can therefore watch an event and then read the full topic rather than receiving an apparently successful response capped to its oldest 200 posts. Use --limit <n> --offset <n> to request one bounded page instead; complete reads fail closed after 10,000 posts or three continuously changing scans rather than consuming memory forever or claiming an inconsistent result.

code connect is the first-party personal Code terminal runtime. Run the exact command copied from Studio → Code → Terminal while your shell is in the local Git checkout. --platform, --app-id, and --env make the requested scope unambiguous; without an explicit app, the CLI can infer it from odla.config.mjs or the Git remote. The terminal proves its isolation engine, builds the CLI-bundled Pi adapter into a content-addressed local image, prepares the digest-pinned build images, and sends an outbound app:code:host:connect request to odla. The app owner approves that real request in Studio. The browser never mints or displays a host bearer.

After approval, the foreground process secret-filters tracked and non-ignored source into a bounded staged snapshot. Harness operates on that frozen copy; the original checkout is never mounted into Pi. Pi receives no network, provider credential, or container-engine socket and accesses source only through the typed CaMeL broker. The session records the trusted Git base, developer-patch digest, snapshot digest, and the combined checkpoint patch. Later local edits do not silently alter an active session. The Pi adapter does not come from a private registry and code connect never logs the developer into one; the only first-run image download is its public, digest-pinned Node base. Use --engine docker only after reviewing that daemon boundary. --once proves enrollment and one heartbeat without accepting queued work.

The approval and host credential records live in odla-ai/db and store hashes, not plaintext. The collected host bearer exists only in the running CLI process: no Code credential file is created under .odla/. A subsequent approved code connect rotates the database-backed credential and invalidates the previous bearer.

init writes:

  • odla.config.mjs
  • src/odla/schema.mjs
  • src/odla/rules.mjs
  • .gitignore entries for local credentials

New configs contain only the dev environment. After the development flow is healthy, initialize the existing app's live instance without editing the config:

npx --yes @odla-ai/cli@latest provision --live --dry-run
npx --yes @odla-ai/cli@latest provision --live --yes --push-secrets

--live targets only prod, enables every configured service, provisions its credentials/schema/integrations, and refuses to create an app when the sandbox does not exist yet. Add prod to envs only when normal provision runs should manage both environments. Calendar apps may predeclare calendar.google.availabilityCalendars.prod while envs remains dev-only; live still uses its own Google consent. Every production mutation requires --yes. Afterwards, explicit live operations such as smoke --env prod, secrets push --env prod --yes, and app go-live work while unqualified commands continue to default to the sandbox.

provision performs the standard safe setup flow:

When using --push-secrets, create the Worker and its Wrangler config first. The CLI checks that config and wrangler whoami before it issues a shown-once credential set.

  1. Gets an odla_dev_... token by email-bound device handshake, or reuses ODLA_DEV_TOKEN / .odla/dev-token.json. A fresh handshake requires --email <account> or ODLA_USER_EMAIL; the matching existing account must sign in, review the exact code, and approve it. Opening the URL alone does not claim the request. Studio shows the immutable project-derived agent handle, project, and capability request without mutation controls. The owner approves that exact request or declines it with an explanation which the CLI returns to the calling agent; a different identity or access set requires a new request. provision includes app.manage for the configured exact app. That permits the non-lifecycle app configuration and sibling-service tenant administration needed by the run; it does not permit ownership, rename/category, archive, restore, or purge. A cached or pending baseline handshake without app.manage is not reused for provision: the CLI starts a fresh request for owner review. An explicit --token or ODLA_DEV_TOKEN has no trustworthy local grant metadata, so a server refusal cannot be repaired by retrying it. Run provision --request-grant --email <account> to ignore the ambient and cached credentials, open a new exact-project review, collect the approved replacement, and continue the same provisioning run. If the exact project id does not exist yet, approval reserves that id for this credential instead of failing: the credential may create only that app once, and Registry binds its reviewed grant to the new app incarnation. Every real CLI handshake prints exactly one canonical /studio?code=… URL. Interactive humans may use the CLI's best-effort browser launch. Agents use the current published CLI with --no-open --wait 600, immediately surface that exact URL and code as a clickable human approval action, and preserve the same foreground process:

    npx --yes @odla-ai/cli@latest auth login --app <appId> --email <odla-account> --no-open --wait 600

    The CLI owns protocol polling. An agent must not call OS open, use browser control, curl a handshake endpoint, build a shell wait loop, detach the process, or start a substitute handshake. The device code stays only in the running process. If that process exits 75, the old code cannot be collected; a later invocation requests a new code and never resumes an older request from .odla/.

  2. Creates the platform app if needed. For a new id, this consumes the exact-id reservation approved in step 1; it is not ambient project-creation authority.

  3. Enables configured services in every configured environment. Calendar config is normalized per env and fixed to Google read-only access.

  4. For local development, mints or reuses that developer's configured service credentials. Multiple developers may hold independent DB/o11y credentials for the same shared sandbox tenant.

  5. Collision-checks and merges declared integration schema/rule fragments, pushes the composed database contract, and creates integration seeds only when their natural-key rows are absent.

  6. Configures hosted app AI by default. An explicit BYOK config stores its provider key in the tenant vault when the configured key env var is set.

  7. For calendar, reads owner-visible connection status and, when needed, asks the platform for a state-bound Google authorization URL and opens only the exact Google OAuth endpoint (or a same-platform interstitial). The human completes consent there; provision follows the exact attempt through initial sync. OAuth codes and refresh tokens never enter the CLI, repo, chat, or app.

  8. Local-only provisioning writes .odla/credentials.local.json with mode 0600. A deployment run with --push-secrets does not write runtime credential plaintext there.

  9. With --write-dev-vars, writes the local Worker values to .dev.vars; with --push-secrets, binds a fresh additive credential set to the exact Cloudflare account/script/environment, transfers both values in one wrangler secret bulk stdin payload, and commits a receipt. A failed push aborts the session and revokes partial issuance.

Plain reruns are idempotent at the tenant/configuration layer. Each deployment receives new, independently revocable runtime credentials; it never rotates a sibling developer or Worker. --rotate-o11y-token remains a destructive legacy recovery operation for locally cached credentials, and --rotate-keys remains the broader local rotation. Neither rotation flag may be combined with --push-secrets. Production mutation or secret transfer requires --yes. --no-write-credentials is valid with --push-secrets because the deployment path is memory-only by design.

Registry staging and Wrangler installation use a short-lived receipt protocol. The CLI preflights the exact target, issues additive credentials, sends one bulk payload, and commits only after Wrangler succeeds. Failure revokes staged credentials; retrying creates a new bounded session and never invalidates a working sibling runtime.

Inventory and retire one successful deployment without disturbing any sibling:

npx --yes @odla-ai/cli@latest credentials list --env dev
npx --yes @odla-ai/cli@latest credentials revoke <receipt-id>

The inventory contains only receipt, target, state, and timestamp metadata. Revocation uses the receipt's exact DB and o11y credential ids; no value is retrieved or rotated.

Verify a deployment that intentionally keeps db/o11y credentials only on the Worker:

npx --yes @odla-ai/cli@latest smoke --env dev --runtime

Runtime smoke never reads .odla/credentials.local.json. It checks Registry public config, the configured Worker link, anonymous integration probes, and owner-visible calendar health. Direct schema and aggregate reads remain part of ordinary local-credential smoke because a runtime credential is deliberately not retrievable from the Worker.

Application capability integrations

An integration is an npm capability composed into an app, not a hosted platform service. Put data-only descriptors under integrations in odla.config.mjs; do not add their ids to services.

import { createCrmIntegration } from "@odla-ai/crm";
import { crm } from "./src/crm.js";

export default {
  app: { id: "my-app", name: "My App" },
  services: ["db"],
  integrations: [createCrmIntegration(crm, { basePath: "/api/crm" })],
  links: { dev: "https://dev.example.com" },
};

Descriptors are consumed structurally, so capability packages do not depend on the CLI. provision --dry-run names every integration and its namespace, seed, and probe counts. Provision accepts an app-authored namespace/rule that is byte-for-byte structurally identical to an integration fragment, which makes existing manually merged apps migratable; conflicting definitions fail before credentials or network mutations. A guarded seed requires a declared-unique natural key, is queried first, and is created only when absent. A concurrent create fails uniqueness instead of overwriting runtime owner edits.

doctor remains offline: it checks the composed schema, complete rule coverage, and seed targets. smoke remains read-only: after the normal db checks it runs each descriptor's anonymous route probe against links.<env>. Installing the package, mounting its routes in the Worker, and supplying app authorization are source-code responsibilities for the agent/developer.

Device-grant security and recovery

The account email sent during a fresh handshake is a non-secret identity hint, not proof of identity. Unknown or never-signed-in accounts never produce a claimable Studio request, and the public start/poll shape intentionally does not reveal whether an account exists. The signed-in matching user must explicitly review the exact code before it becomes pending, and only one claimed pending or approved-but-uncollected request can be active for that user. For an ordinary request, Studio also requires at least one exact project selection. The credential receives project metadata, PM, discussion, and brand-candidate editing grants only for those projects, never ambient owner access, app administration, or brand approval. A selected id that does not yet exist is a one-time bootstrap reservation: the credential may create that exact app, after which the grant is bound to its new lifetime. A 404 from the approve action is a routing regression; missing, archived, foreign, changed, or raced requests must produce a specific conflict or denial. The reservation authorizes only that bootstrap creation; later service, configuration, and administrative mutations still require their normal capabilities and human checkpoints.

Developer grants are tracked by id, owner, label, scopes, creation/expiry, last use, and revocation state. The plaintext token is delivered once to the polling client and is never available from the inventory. Every signed-in user can inspect and revoke their own grants under Your agent credentials in Studio; platform admins additionally see the global token inventory and metadata-only attempt evidence. Future requests with a revoked token fail. Deleting .odla/dev-token.json or .odla/admin-token.local.json removes the local copy but does not revoke a token already copied elsewhere, so use Studio revocation when compromise or accidental approval is possible.

smoke verifies a provisioned environment from local credentials. It fetches the platform public config, checks configured service credentials and AI provider, exercises schema and aggregate checks only when db is enabled, checks owner-visible booking health when calendar is enabled, and executes declared integration route probes without credentials. An o11y-only project therefore does not need or mint a db key. It fails early with a clear message when provisioning has not written .odla/credentials.local.json.

Google Calendar booking

Calendar is a platform-custodied connector, not a Google credential stored by the application. Enable both db and calendar, configure calendar ids per environment, then run normal provision. The first run has two distinct human checkpoints: the odla device code establishes app-owner authority, and the subsequent server-issued Google page grants calendar.events and calendar.freebusy consent so availability and booking writes stay server-side.

For parity with a static Google Appointment Schedule embed/link, set the public bookingPageUrl per environment. Provision applies it through the owner-only calendar settings route; it is public configuration, not a credential.

calendar status returns the safe connection/bookability projection; add --json for agent automation. Once connected, calendar calendars lists ids visible to the Google identity so the checked-in selection can be refined. calendar connect applies the checked-in booking-page setting and starts consent when the connection is absent, failed, disconnected, or degraded (so an agent can repair revoked credentials); a healthy connection is reused. A connection that already holds provider credentials is reused without requesting Google consent again, and an in-flight authorization is awaited rather than duplicated.

Nothing syncs: Google Calendar is the source of truth and odla stores no events and no attendees, so there is no resync to ask for. calendar disconnect --yes deletes this app/environment connection's encrypted platform token. It does not revoke the shared user-to-Google-OAuth-project grant, because that could invalidate other odla connections for the same user. A future explicitly global revoke command would need cross-connection accounting and warning. A direct production connect requires --yes. None of these commands accepts a Google authorization code, client secret, access token, or refresh token.

The SDK uses the existing server-side db values (ODLA_ENDPOINT, ODLA_TENANT, ODLA_API_KEY), so calendar adds no Worker secret. Never expose ODLA_API_KEY to a browser. Trusted Worker code initializes @odla-ai/calendar for live FreeBusy/upcoming reads and idempotent create/reschedule/cancel operations; browser code imports only pure helpers and public configuration from @odla-ai/calendar/client.

These CLI commands manage the provider connection and its non-secret calendar selection. They intentionally do not create or mutate events: application backends do that through @odla-ai/calendar. Google is the source of truth and odla stores no event or attendee mirror.

The bundled build/migration skills add a passive pre-ship gate with @odla-ai/security: install it with a normal development range, commit the lockfile, then run npm i -D @odla-ai/security, followed by npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical. The passive scan remains a separate binary: it makes no model calls and does not execute target code. After explicit redacted-source approval, odla-ai security run . --env dev --ack-redacted-source adds hosted discovery and independent validation. The CLI obtains/reuses app-owner auth and the platform supplies bounded role grants; it never requests provider keys. The hosted run accounts for both immutable role ceilings: discovery reserves one call for recon, caps first-pass hunts to what remains, and uses only residual calls for retries; validation reviews the highest-risk candidates first. The console and artifacts show used/skipped calls, and a budget-limited run is incomplete unless explicitly reviewed with --allow-incomplete. All hosted-security network calls target the configured odla platform origin (/registry/security/runs, /registry/ai/extract, and completion). The CLI never contacts a model-provider host; the odla.ai broker alone owns the typed security prompts/schemas and resolves the admin-selected route and vaulted credential. A cached or environment token is audience-bound and is never sent to a different --platform origin. Early-access run ceilings are rolling. A platform-ceiling 429 always carries a conservative Retry-After. A provider-side 429 uses the sanitized provider_rate_limited code after the provider SDK's bounded retries and carries a bounded Retry-After only when the upstream supplied one. The CLI does not echo arbitrary response text or silently change the admin-selected model. Confirm npm view @odla-ai/security version succeeds and record the installed version from npm ls @odla-ai/security with the scan evidence. A registry failure blocks the preflight; it is not a clean scan. odla's own engineering environment uses an equivalent internal gate; customer projects should use the published odla-security command above.

For a server-side, commit-pinned review, connect one repository through the source-read-only odla GitHub App. The CLI infers owner/name from a safe GitHub HTTPS or SSH origin; pass --repo owner/name when it cannot. It opens GitHub's installation/authorization page and polls that exact odla attempt:

npx --yes @odla-ai/cli@latest security github connect --env dev --no-open
npx --yes @odla-ai/cli@latest security plan --env dev
npx --yes @odla-ai/cli@latest security sources --env dev
npx --yes @odla-ai/cli@latest security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source

security plan is the owner-readable disclosure preflight. It shows the exact admin-selected discovery and validation provider/model, immutable policy versions, per-route call/input/output bounds, prompt bundle, redaction/report contracts, credential readiness, provider independence, report retention, the no-target-execution boundary, and a digest covering that complete processing contract. security sources includes the same plan (and returns { plan, sources } with --json). A source run fetches it again and refuses to enqueue while either route is disabled, lacks a usable platform credential, or is not independently routed. Copy the displayed digest into --plan-digest; the CLI requires it in addition to --ack-redacted-source and refuses to let a generic acknowledgement silently approve a refetched plan. The enqueue request carries the plan digest and both expected policy versions. After verifying the user-supplied plan digest, the CLI fetches and prints odla.ai's exact source/ref/profile execution intent. A second server-computed digest binds that selection, the redacted disclosure, and the plan digest; the CLI sends it as expectedExecutionDigest. A concurrent selection or admin change returns intent_conflict, plan_conflict, policy_conflict, or security_ai_not_ready instead of silently switching providers or models. Fetch and review a fresh security plan, then explicitly acknowledge its new digest. An omitted --ref is previewed as the source's explicit current default-branch name. If that default changes before enqueue, odla.ai rejects the stale intent; it never defers the choice to GitHub HEAD at Workflow execution time.

The source job defaults to follow, renders the normalized report, and applies the same --fail-on high, --fail-on-candidates critical, and incomplete coverage gate; use --no-follow only to enqueue. security status <job-id> and security report <job-id> recover or inspect a prior job. security github disconnect --source <source-id> --env dev --yes revokes odla's saved source without requiring a GitHub-side uninstall.

The client sends only the opaque odla sourceId, ref, profile, and explicit sourceDisclosure: "redacted"; it has no PAT, GitHub installation id, provider key, provider, or model flag. odla.ai verifies repository access, resolves the ref to an immutable commit SHA, fetches the bounded snapshot server-side, and routes bounded best-effort credential-pattern-redacted snippets through the admin-selected System AI discovery and independent-validation models. GitHub read authorization is not source disclosure consent, which is why --ack-redacted-source remains required. The retained schema has no archive, file-body, explicit source-excerpt, context packet, raw provider-response, execution-trace, or reproduction-output field. The private normalized report does retain bounded best-effort credential-pattern-redacted model-derived prose and repository-relative paths for up to 90 days, so treat it as sensitive because that prose can reveal source semantics or an unrecognized secret. GitHub and provider retention/residency terms remain separate. GitHub Checks contain counts/coverage and link to the authenticated Studio report. Target code is not executed, and even complete coverage is not proof of security; unscheduled, shallow, blocked, budget-exhausted, and projection- truncated work stays visible.

admin ai show|models|set|credentials|credential set|usage|audit manages platform-funded System AI (o11y.triage, security.discovery, security.validation). It uses a separate, mode-0600 scoped-grant cache: beneath the selected named context, the project root when a config is loaded, or the global operator directory otherwise. It never follows an arbitrary shell working directory. Studio displays the exact scope, only an admin can approve it, and it expires after ~15 minutes; policy, credential, and usage capabilities are separate and cannot act as the approver on ordinary app/db/o11y routes. admin ai models reads the server catalog. For app code and provisioning, ai models [--env dev] [--json] reads the app-safe public configuration and lists only the hosted models that exact app may select (or the configured provider's local catalog in BYOK mode), with the default and generic capabilities. admin ai set security updates discovery and independent validation together with expected revisions; a concurrent edit returns 409 and reloads instead of being overwritten. admin ai usage requests its own read-only capability and lists metadata-only events and status aggregates; narrow it with --app-id, --env, --run-id, and --limit, or use --json for automation. admin ai audit separately reads immutable metadata-only policy and credential change events with actor attribution; credential values never enter that trail. It never returns prompts, repository source, model output, reports, or provider credentials. Credential writes accept only --from-env <NAME> or --stdin and never place the value in argv, output, or the cache. System AI and hosted app AI have separate routes and accounting; explicit BYOK has its own app-tenant vault path. Discovery and validation provider/model settings are authoritative server policy: a security command has no field that can override either route. Admin bounds can only narrow the hard ceilings of 64 calls per role/run and, for each call, 512 KiB input and 32,768 output tokens. For a non-default platform, environment credentials must also declare the matching ODLA_ADMIN_TOKEN_AUDIENCE.

secrets push --env <env> is the narrower recovery/retry command. It moves the configured env's odla-db key and/or o11y ingest token from .odla/credentials.local.json into the deployed Worker by piping each value over stdin to wrangler secret put; values never appear on argv, in output, or in an agent's transcript. It preflights wrangler whoami and the presence of a wrangler config file. The env prod/production targets the top-level wrangler environment (no --env flag is passed to Wrangler) and requires --yes; every other env maps to wrangler --env <name>. --dry-run prints a redacted plan without spawning anything. It is only for an already-saved local credential. For a new Worker or a lost local plaintext, use provision --push-secrets: the handshake authorizes new target-bound credentials and no local production cache is required.

secrets set <name> --env <env> stores one named secret in that env's tenant vault — the same write-only slot Studio's Secrets panel fills (for example clerk_webhook_secret for synced auth). The value may come only from --from-env <NAME> or --stdin, so a producer command pipes straight into the vault without the secret ever appearing on argv, in output, or in an agent's transcript; it is encrypted at rest and readable back only by that tenant's app API key, never by the CLI, Studio, or the developer token. $-prefixed names are platform-reserved. secrets set-clerk-key --env <env> stores the app's Clerk secret key in the reserved $clerk_secret slot so the platform can resolve the app's users (invites, member lookups); reserved secrets are never readable by app keys. It refuses an sk_test_ key for a prod-named env and requires --yes to put an sk_live_ key into a non-prod env, since live users would sync into a non-prod tenant. Both commands authenticate with the developer token ($ODLA_DEV_TOKEN, the cached token, or a fresh device approval), and prod-named envs require --yes, matching secrets push.

Who does what

If a change is deterministic from odla.config.mjs, the CLI owns it: app and service enablement, credential issuance and local persistence, .dev.vars, schema/rules/auth/AI/calendar configuration, owner-safe consent orchestration, and Wrangler secret transfer. The coding agent owns application semantics: installing @odla-ai/o11y, wrapping the Worker with withObservability, and choosing useful spans, metrics, errors, and LLM-usage records. The human owns the device approval, production consent, and explicit destructive rotation. Studio is where people view telemetry and perform manual recovery, configure System AI, and inspect app/run-attributed platform usage; its o11y token control is not the normal setup path. Run npx --yes @odla-ai/cli@latest capabilities for the human-readable contract or add --json when an agent or tool needs to branch on it without scraping prose.

setup (and skill install) installs one complete offline runbook bundle at .agents/skills/, then adds thin native adapters for Claude Code (.claude/skills/), Codex, Cursor (.cursor/rules/), GitHub Copilot (`.github/copilot