@odla-ai/cli 0.4.0 → 0.6.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/llms.txt CHANGED
@@ -1,16 +1,19 @@
1
1
  # @odla-ai/cli — LLM context
2
2
 
3
- > Project-neutral CLI for provisioning odla apps, database schemas, AI settings, and local runtime files.
3
+ > Agent-operable CLI for odla provisioning, scoped System AI administration, hosted security runs, Worker secrets, and smoke checks.
4
4
 
5
- > ⚠️ **Experimentalan agentic-coding experiment.** This package is built and
6
- > operated by autonomous coding agents as an experiment in agentic loops. APIs
7
- > change without notice and nothing here is production-hardened. **Use at your
8
- > own risk.**
5
+ > ⚠️ **Early access pre-1.0.** Agents work from bounded runbooks; humans
6
+ > approve credentials, production changes, releases, and merges. APIs and exact
7
+ > package availability can change. Review the documented guarantees and
8
+ > limitations; this software is MIT-licensed and provided without warranty.
9
9
 
10
10
  Project-neutral provisioning CLI for odla apps. It creates and validates an
11
11
  `odla.config.mjs`, then uses that config to register an app, enable services,
12
- push odla-db schema/rules, configure platform AI, configure Clerk auth, record
13
- deployment links, and write local runtime credentials.
12
+ push odla-db schema/rules, configure per-app BYOK AI, configure Clerk auth, record
13
+ deployment links, provision o11y ingest credentials, and transfer local or
14
+ deployed Worker secrets without printing them. It also gives platform admins a
15
+ scoped System AI control surface and gives app owners a provider-key-free
16
+ hosted security command.
14
17
 
15
18
  It does not know about any specific app. App identity, environments, services,
16
19
  schema, rules, auth, AI provider, and links all come from config.
@@ -28,7 +31,8 @@ For a project you keep working in, install it as a dev dependency so the
28
31
  shorter `npx odla-ai` form works and the version is pinned by your lockfile:
29
32
 
30
33
  ```bash
31
- npm i -D @odla-ai/cli
34
+ npm view @odla-ai/cli@0.6.0 version
35
+ npm i -D --save-exact @odla-ai/cli@0.6.0
32
36
  ```
33
37
 
34
38
  ## Prerequisites
@@ -48,12 +52,20 @@ npm i -D @odla-ai/cli
48
52
  ## Commands
49
53
 
50
54
  ```bash
55
+ npx odla-ai setup
51
56
  npx odla-ai init --app-id my-app --name "My App"
52
57
  npx odla-ai doctor
58
+ npx odla-ai capabilities --json
59
+ # install SDKs, write the Worker, and create wrangler.jsonc before secret push
53
60
  npx odla-ai provision --dry-run
54
- npx odla-ai provision --write-dev-vars
61
+ npx odla-ai provision --write-dev-vars --push-secrets
55
62
  npx odla-ai smoke --env dev
56
63
  npx odla-ai secrets push --env dev
64
+ npx odla-ai security run . --env dev --ack-redacted-source
65
+ npx odla-ai admin ai show
66
+ npx odla-ai admin ai set security.discovery --provider anthropic --model claude-opus-4-8 --enabled
67
+ npx odla-ai admin ai credentials
68
+ npx odla-ai admin ai credential set anthropic --from-env ANTHROPIC_API_KEY
57
69
  npx odla-ai skill install
58
70
  npx odla-ai version
59
71
  ```
@@ -65,8 +77,16 @@ npx odla-ai version
65
77
  - `src/odla/rules.mjs`
66
78
  - `.gitignore` entries for local credentials
67
79
 
80
+ New configs contain only the `dev` environment. Add `prod` explicitly after
81
+ the development flow is healthy; `provision` refuses to mutate `prod` or
82
+ `production` without `--yes` and supports `--dry-run` for review.
83
+
68
84
  `provision` performs the standard safe setup flow:
69
85
 
86
+ When using `--push-secrets`, create the Worker and its Wrangler config first.
87
+ The CLI checks that config and `wrangler whoami` before it issues or rotates a
88
+ shown-once credential.
89
+
70
90
  1. Gets an `odla_dev_...` token by device handshake, or reuses
71
91
  `ODLA_DEV_TOKEN` / `.odla/dev-token.json`.
72
92
  In an interactive terminal it opens the approval page in your browser when
@@ -75,14 +95,28 @@ npx odla-ai version
75
95
  best-effort; the printed URL and code always remain the fallback.
76
96
  2. Creates the platform app if needed.
77
97
  3. Enables configured services in every configured environment.
78
- 4. Mints or reuses each env's odla-db app key.
98
+ 4. Mints or reuses each env's configured service credentials: an odla-db app
99
+ key for `db` and an ingest token for `o11y`.
79
100
  5. Pushes the configured schema and rules.
80
101
  6. Configures platform AI and stores provider keys in the tenant vault when the
81
102
  configured key env var is set.
82
103
  7. Writes `.odla/credentials.local.json` with mode `0600`.
83
-
84
- Pass `--rotate-keys` to mint fresh db keys. Pass `--no-write-credentials` to
85
- skip writing the local credentials file.
104
+ 8. With `--write-dev-vars`, writes the local Worker values to `.dev.vars`; with
105
+ `--push-secrets`, transfers the configured service secrets to the matching
106
+ deployed Worker environment over Wrangler stdin.
107
+
108
+ Plain reruns are idempotent. Pass `--rotate-o11y-token` only when deliberately
109
+ invalidating and replacing the o11y token; combine it with `--push-secrets` so
110
+ the replacement reaches the Worker in the same run. `--rotate-keys` remains the
111
+ broader credential rotation. Production mutation or secret transfer requires
112
+ `--yes`. Pass `--no-write-credentials` to skip writing the local credentials
113
+ file; it cannot be combined with an operation that would issue a shown-once
114
+ credential.
115
+
116
+ Rotation and Wrangler secret replacement are not a distributed transaction.
117
+ The CLI preflights Wrangler first and persists the replacement before pushing;
118
+ if that final push still fails, follow the printed `secrets push --env ...`
119
+ retry. Do not run the rotation flag again.
86
120
 
87
121
  `smoke` verifies a provisioned environment from local credentials. It fetches
88
122
  the platform public config, checks the configured AI provider, fetches the live
@@ -90,23 +124,82 @@ odla-db schema with the tenant key, compares expected schema entities, and runs
90
124
  a count aggregate against the first entity. It fails early with a clear message
91
125
  when provisioning has not written `.odla/credentials.local.json`.
92
126
 
93
- `secrets push --env <env>` moves the env's odla-db key from
94
- `.odla/credentials.local.json` into the deployed Worker by piping it over
95
- stdin to `wrangler secret put ODLA_API_KEY` the value never appears on
96
- argv, in output, or in an agent's transcript. It preflights `wrangler whoami`
97
- and the presence of a wrangler config file. The env `prod`/`production`
98
- targets the top-level wrangler environment (no `--env` flag is passed to
99
- wrangler) and requires `--yes`; every other env maps to wrangler's
100
- `--env <name>`. `--dry-run` prints a redacted plan without spawning anything.
101
-
102
- `skill install` copies the Claude Code skills bundled with this package
103
- (currently `odla-migrate`, the phased GitHub Pages odla migration
104
- procedure) into the project's `.claude/skills/`, or into `~/.claude/skills/`
105
- with `--global`. Re-runs are idempotent; a locally modified skill file is
106
- never overwritten without `--force`. The skill's reference runbook is served
107
- at <https://odla.ai/llms-migrate.txt>; the human companion "Moving your site
108
- to odla" (accounts, the approval code, costs) lives in the odla docs after
109
- sign-in.
127
+ The bundled build/migration skills add a passive pre-ship gate with
128
+ `@odla-ai/security`: after the exact-version availability check, run
129
+ `npm i -D --save-exact @odla-ai/security@0.2.0`, then
130
+ `npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical`.
131
+ The passive scan remains a separate binary: it makes no model calls and does
132
+ not execute target code. After explicit redacted-source approval,
133
+ `odla-ai security run . --env dev --ack-redacted-source` adds hosted discovery
134
+ and independent validation. The CLI obtains/reuses app-owner auth and the
135
+ platform supplies bounded role grants; it never requests provider keys.
136
+ Early-access run ceilings are rolling; a `429` includes the platform's safe
137
+ retry hint, which the CLI surfaces without echoing arbitrary response text.
138
+ Before installing the exact release, require
139
+ `npm view @odla-ai/security@0.2.0 version` to succeed before installation. An
140
+ exact-version `E404` means the release is unavailable, not that the security
141
+ preflight passed, and does not prove the package name is absent. odla's own
142
+ engineering environment uses an equivalent internal gate; customer projects
143
+ should use the published `odla-security` command above.
144
+
145
+ `admin ai show|set|credentials|credential set` manages platform-funded System
146
+ AI (`o11y.triage`, `security.discovery`, `security.validation`). It uses a
147
+ separate, mode-`0600` `.odla/admin-token.local.json` cache. Studio displays the
148
+ exact scope, only an admin can approve it, and it expires after ~15 minutes;
149
+ the token never becomes ambient admin. Credential writes accept only
150
+ `--from-env <NAME>` or `--stdin` and never place the value in argv, output, or
151
+ the cache. System AI and each app's BYOK AI are separate vault/funding paths.
152
+
153
+ `secrets push --env <env>` is the narrower recovery/retry command. It moves the
154
+ configured env's odla-db key and/or o11y ingest token from
155
+ `.odla/credentials.local.json` into the deployed Worker by piping each value
156
+ over stdin to `wrangler secret put`; values never appear on argv, in output, or
157
+ in an agent's transcript. It preflights `wrangler whoami` and the presence of a
158
+ wrangler config file. The env `prod`/`production` targets the top-level
159
+ wrangler environment (no `--env` flag is passed to Wrangler) and requires
160
+ `--yes`; every other env maps to `wrangler --env <name>`. `--dry-run` prints a
161
+ redacted plan without spawning anything. For the normal path, use
162
+ `provision --write-dev-vars --push-secrets` so service enablement, credential
163
+ issuance, local config, and deployed secret transfer stay one composition.
164
+
165
+ ## Who does what
166
+
167
+ If a change is deterministic from `odla.config.mjs`, the CLI owns it: app and
168
+ service enablement, credential issuance and local persistence, `.dev.vars`,
169
+ schema/rules/auth/AI configuration, and Wrangler secret transfer. The coding
170
+ agent owns application semantics: installing `@odla-ai/o11y`, wrapping the
171
+ Worker with `withObservability`, and choosing useful spans, metrics, errors, and
172
+ LLM-usage records. The human owns the device approval, production consent, and
173
+ explicit destructive rotation. Studio is where people view telemetry and
174
+ perform manual recovery, configure System AI, and inspect app/run-attributed
175
+ platform usage; its o11y token control is not the normal setup path.
176
+ Run `npx odla-ai capabilities` for the human-readable contract or add `--json`
177
+ when an agent or tool needs to branch on it without scraping prose.
178
+
179
+ `setup` (and `skill install`) installs one complete offline runbook bundle at
180
+ `.agents/skills/`, then adds thin native adapters for Claude Code
181
+ (`.claude/skills/`), Codex, Cursor (`.cursor/rules/`), GitHub Copilot
182
+ (`.github/copilot-instructions.md`), Gemini CLI (`GEMINI.md`), and portable
183
+ `AGENTS.md` agents. The default is `--agent all`; select one or more with
184
+ `--agent claude,codex` or repeated `--agent` flags. A global install supports
185
+ the native Claude and Codex roots only.
186
+
187
+ Existing `AGENTS.md`, `GEMINI.md`, and Copilot instructions are preserved. The
188
+ installer appends a bounded odla-managed section, is idempotent, refuses
189
+ symlink-redirection and locally changed managed content, and lets `--force`
190
+ replace only odla-owned content. The local bundle includes build, migration,
191
+ and observability-debug procedures plus every reference they need; agents do
192
+ not need online docs or access to odla's private service source after setup.
193
+
194
+ After running setup, the whole prompt can be: “Set up odla in this repo using
195
+ the installed local odla runbook. Decide whether this is a new app or an
196
+ existing site to migrate, then drive it end to end. Ask me only for the device
197
+ approval and other human-only checkpoints.”
198
+
199
+ For programmatic callers, `installSkill()` keeps `targetDir`, `written`, and
200
+ `unchanged` as one unambiguous primary-root view: the latter two contain paths
201
+ relative to `targetDir` only. Use the additive absolute `writtenPaths` and
202
+ `unchangedPaths` fields to inspect every shared-bundle and adapter artifact.
110
203
 
111
204
  `doctor` also lints for common footguns: permission rules that are literally
112
205
  `"true"` (public writes always warn; a public `view` warns unless the
@@ -124,8 +217,8 @@ export default {
124
217
  platformUrl: process.env.ODLA_PLATFORM_URL ?? "https://odla.ai",
125
218
  dbEndpoint: process.env.ODLA_ENDPOINT ?? process.env.ODLA_DB_ENDPOINT ?? "https://db.odla.ai",
126
219
  app: { id: "my-app", name: "My App" },
127
- envs: ["prod", "dev"],
128
- services: ["db", "ai"],
220
+ envs: ["dev"], // add "prod" deliberately; provisioning it requires --yes
221
+ services: ["db", "ai", "o11y"],
129
222
  db: {
130
223
  schema: "./src/odla/schema.mjs",
131
224
  rules: "./src/odla/rules.mjs",
@@ -137,6 +230,8 @@ export default {
137
230
  keyEnv: "ANTHROPIC_API_KEY",
138
231
  },
139
232
  auth: {
233
+ // Publishable key (public). Created + pulled by the Clerk CLI (`npx clerk`);
234
+ // See the installed odla-migrate phase 3. Inline `pk_…` or "$ENV_VAR".
140
235
  clerk: {
141
236
  dev: "$CLERK_PUBLISHABLE_KEY",
142
237
  prod: "$CLERK_PUBLISHABLE_KEY",
@@ -156,15 +251,123 @@ If schema is present and rules are omitted, `defaultRules: "deny"` generates
156
251
  deny-all rules for every schema entity. That is the safe default for Workers
157
252
  that mediate reads and writes with an app key.
158
253
 
159
- ## API reference (generated from dist/index.d.ts, v0.4.0)
254
+ ## API reference (generated from dist/index.d.ts, v0.6.0)
160
255
 
161
256
  ```ts
257
+ import { SecurityReport, PlatformSecurityRun } from '@odla-ai/security';
258
+
259
+ /**
260
+ * Parse CLI arguments and dispatch to the matching command — the entry point
261
+ * `bin.ts` invokes. `argv` defaults to `process.argv.slice(2)`; the first
262
+ * positional selects the command and the rest are parsed into flags (supporting
263
+ * `--flag value`, `--flag=value`, boolean `--flag`, `--no-flag`, and repeated
264
+ * flags collected into arrays).
265
+ *
266
+ * Recognized commands: `version`/`-v`/`--version` and `help`/`-h`/`--help` print
267
+ * and return; `init` scaffolds a project (requires `--app-id` and `--name`);
268
+ * `doctor` validates config offline; `capabilities` prints the responsibility
269
+ * boundary; `admin ai` reads/updates purpose-specific platform AI policies and
270
+ * write-only credentials through a short-lived exact-scope device grant;
271
+ * `security run` performs an explicitly acknowledged, app-attributed hosted
272
+ * reasoning pass; `provision` runs the full provision (including optional secret
273
+ * rotation, local vars, and deployed secret push); `smoke` checks a live env;
274
+ * `setup` and `skill install` install the bundled offline runbooks for Claude
275
+ * Code, Codex, Cursor, Copilot, Gemini, and `AGENTS.md`-aware agents; and
276
+ * `secrets push` pipes the env's configured db/o11y secrets into the Worker via
277
+ * Wrangler (requires `--env`). Throws on an unknown command/subcommand, unsupported option, extra
278
+ * positional argument, missing required value, or a dispatched command error.
279
+ *
280
+ * @param argv Argument vector without the node/script prefix (default
281
+ * `process.argv.slice(2)`).
282
+ */
162
283
  declare function runCli(argv?: string[]): Promise<void>;
163
284
 
285
+ /** Hosted platform purposes whose provider, model, and budgets an admin may route. */
286
+ declare const SYSTEM_AI_PURPOSES: readonly ["o11y.triage", "security.discovery", "security.validation"];
287
+ /** One catalog-validated, platform-funded System AI purpose. */
288
+ type SystemAiPurpose = (typeof SYSTEM_AI_PURPOSES)[number];
289
+ /** Inputs for one System AI read, policy update, credential-status read, or
290
+ * write-only credential transfer. Provider values may come only from a named
291
+ * environment variable or stdin. */
292
+ interface AdminAiOptions {
293
+ action: "show" | "set" | "credentials" | "credential-set";
294
+ purpose?: string;
295
+ provider?: string;
296
+ model?: string;
297
+ enabled?: boolean;
298
+ maxInputBytes?: number;
299
+ maxOutputTokens?: number;
300
+ maxCallsPerRun?: number;
301
+ platform?: string;
302
+ token?: string;
303
+ json?: boolean;
304
+ open?: boolean;
305
+ fetch?: typeof fetch;
306
+ stdout?: Pick<typeof console, "log">;
307
+ openApprovalUrl?: (url: string) => Promise<void>;
308
+ credentialProvider?: string;
309
+ fromEnv?: string;
310
+ stdin?: boolean;
311
+ readStdin?: () => Promise<string>;
312
+ /** Separate short-lived admin-token cache; never reuses the app developer-token file. */
313
+ tokenFile?: string;
314
+ }
315
+ /** Inputs for obtaining one short-lived, exact-scope platform device grant. */
316
+ interface ScopedPlatformTokenOptions {
317
+ platform: string;
318
+ scope: "platform:ai:read" | "platform:ai:write" | "platform:security:self";
319
+ open?: boolean;
320
+ fetch?: typeof fetch;
321
+ stdout?: Pick<typeof console, "log">;
322
+ openApprovalUrl?: (url: string) => Promise<void>;
323
+ tokenFile?: string;
324
+ }
325
+ /** Obtain/cache one exact platform scope without granting ambient admin. */
326
+ declare function getScopedPlatformToken(options: ScopedPlatformTokenOptions): Promise<string>;
327
+ /** Read or update platform-funded AI routing. Authentication is a short-lived,
328
+ * explicitly scoped device grant; ordinary app developer tokens never become
329
+ * platform admins. Credential writes accept values only from a named env var
330
+ * or stdin, transfer them once, and never place them in argv, output, or the
331
+ * scoped-token cache. */
332
+ declare function adminAi(options: AdminAiOptions): Promise<void>;
333
+
334
+ /** Stable, machine-readable division of work between the CLI, coding agent,
335
+ * human operator, and Studio. Printed by `odla-ai capabilities --json`. */
336
+ declare const CAPABILITIES: {
337
+ readonly cli: readonly ["register the app and enable configured services per environment", "issue, persist, and explicitly rotate configured service credentials", "write local Worker values and push deployed Worker secrets through Wrangler stdin", "push db schema/rules and configure platform AI, auth, and deployment links", "validate config offline and smoke-test a provisioned db environment", "run app-attributed hosted security discovery and independent validation without provider keys", "let admins manage system AI routes/credentials through short-lived exact-scope approval"];
338
+ readonly agent: readonly ["install and import the selected odla SDKs", "wrap the Worker with withObservability and choose useful telemetry", "make application-specific schema, rules, auth, UI, and migration decisions"];
339
+ readonly human: readonly ["approve the odla device code and one-off third-party logins", "consent to production changes with --yes", "request destructive credential rotation explicitly", "review application semantics, security findings, releases, and merges", "approve redacted-source disclosure before hosted security reasoning"];
340
+ readonly studio: readonly ["view telemetry and environment state", "perform manual credential recovery when the CLI's local shown-once copy is unavailable", "configure system AI purposes and view app/environment/run-attributed usage"];
341
+ };
342
+ type Output = Pick<typeof console, "log">;
343
+ /** Print the stable responsibility boundary for humans or agent tooling. */
344
+ declare function printCapabilities(json?: boolean, out?: Output): void;
345
+
164
346
  interface DoctorOptions {
165
347
  configPath: string;
166
348
  stdout?: Pick<typeof console, "log" | "error">;
167
349
  }
350
+ /**
351
+ * Validate and summarize an odla project config entirely offline — no network
352
+ * calls and no file writes. Loads `configPath`, builds the provision plan, and
353
+ * resolves the schema and rules (synthesizing deny-all rules from the schema
354
+ * when `db.rules` is omitted and `db.defaultRules` isn't `false`), then prints a
355
+ * summary line for the app, envs, services, schema entity count, rules namespace
356
+ * count, and AI provider.
357
+ *
358
+ * It then collects warnings for the mistakes provision can't fix silently: a
359
+ * schema with no entities, a rules namespace with no matching entity, the `ai`
360
+ * service enabled without an `ai.provider`, `auth.clerk` entries that reference
361
+ * an unset env var (`$FOO`), an `ai.keyEnv` that isn't set (provider key upload
362
+ * will be skipped), and — when `o11y` is enabled — any env whose local
363
+ * credentials lack an ingest token. It also folds in the shared rule linter,
364
+ * git-tracked secret-shaped files, and wrangler config warnings. Prints `ok`
365
+ * when clean, otherwise the warning list. Purely advisory: never throws on
366
+ * warnings and never mutates anything.
367
+ *
368
+ * @param options.configPath Path to the `odla.config.mjs` to inspect.
369
+ * @param options.stdout Optional console-like sink (defaults to `console`).
370
+ */
168
371
  declare function doctor(options: DoctorOptions): Promise<void>;
169
372
 
170
373
  interface InitOptions {
@@ -178,8 +381,44 @@ interface InitOptions {
178
381
  force?: boolean;
179
382
  stdout?: Pick<typeof console, "log" | "error">;
180
383
  }
384
+ /**
385
+ * Scaffold a new odla project into `rootDir` (default: cwd). Writes an
386
+ * `odla.config.mjs` (default name, overridable via `configPath`) plus starter
387
+ * `src/odla/schema.mjs` (a single `notes` entity) and `src/odla/rules.mjs`
388
+ * (deny-all), and creates the `src/odla` and `.odla` directories. Also runs
389
+ * `ensureGitignore` so the local token/credentials paths are ignored.
390
+ *
391
+ * Fully local and synchronous — no network calls. The config file is only
392
+ * written when it doesn't already exist unless `force` is set (otherwise it
393
+ * throws); the schema/rules files are written only when missing (never
394
+ * overwritten, even with `force`). `appId` must match `^[a-z0-9][a-z0-9-]*$` or
395
+ * it throws. Defaults: `envs` → `["dev"]`, `services` → `["db","ai"]`,
396
+ * `aiProvider` → `"anthropic"` (which also picks the `keyEnv`, e.g.
397
+ * `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`/`GOOGLE_API_KEY`).
398
+ *
399
+ * @param options.appId Slug for the app (lowercase alphanumerics and hyphens).
400
+ * @param options.name Human-readable app name embedded in the config.
401
+ * @param options.rootDir Project root (defaults to `process.cwd()`).
402
+ * @param options.configPath Config filename relative to root (default `odla.config.mjs`).
403
+ * @param options.envs Environment names (default `["dev"]`; production is an explicit opt-in).
404
+ * @param options.services Enabled services (default `["db","ai"]`).
405
+ * @param options.aiProvider AI provider slug (default `"anthropic"`).
406
+ * @param options.force Overwrite an existing config file instead of throwing.
407
+ * @param options.stdout Optional console-like sink (defaults to `console`).
408
+ */
181
409
  declare function initProject(options: InitOptions): void;
182
410
 
411
+ interface RunResult {
412
+ code: number;
413
+ stdout: string;
414
+ stderr: string;
415
+ }
416
+ /** Subprocess seam: injectable in tests, `defaultRunner` in production. */
417
+ type CommandRunner = (cmd: string, args: string[], opts?: {
418
+ input?: string;
419
+ cwd?: string;
420
+ }) => Promise<RunResult>;
421
+
183
422
  type OdlaEnvName = string;
184
423
  type AppRules = Record<string, {
185
424
  view?: string;
@@ -192,6 +431,7 @@ interface ClerkAuthConfig {
192
431
  audience?: string;
193
432
  mode?: "client" | "full";
194
433
  }
434
+ /** Authored `odla.config.mjs` contract consumed by doctor and provision. */
195
435
  interface OdlaProjectConfig {
196
436
  platformUrl?: string;
197
437
  dbEndpoint?: string;
@@ -238,6 +478,7 @@ interface OdlaProjectConfig {
238
478
  gitignore?: boolean;
239
479
  };
240
480
  }
481
+ /** Normalized project config with resolved paths, endpoints, envs, and services. */
241
482
  interface LoadedProjectConfig extends OdlaProjectConfig {
242
483
  configPath: string;
243
484
  rootDir: string;
@@ -252,6 +493,7 @@ interface LoadedProjectConfig extends OdlaProjectConfig {
252
493
  gitignore: boolean;
253
494
  };
254
495
  }
496
+ /** Private mode-0600 local state written by provision and never committed. */
255
497
  interface LocalCredentials {
256
498
  appId: string;
257
499
  platformUrl: string;
@@ -263,10 +505,15 @@ interface LocalCredentials {
263
505
  o11yToken?: string;
264
506
  }>;
265
507
  }
508
+ /** Controls one dev-first provisioning run; production mutation requires `yes`. */
266
509
  interface ProvisionOptions {
267
510
  configPath: string;
268
511
  dryRun?: boolean;
269
512
  rotateKeys?: boolean;
513
+ /** Rotate only the o11y ingest token; leaves db keys unchanged. */
514
+ rotateO11yToken?: boolean;
515
+ /** Push configured Worker secrets to every configured env after provisioning. */
516
+ pushSecrets?: boolean;
270
517
  writeCredentials?: boolean;
271
518
  writeDevVars?: string | boolean;
272
519
  token?: string;
@@ -276,10 +523,14 @@ interface ProvisionOptions {
276
523
  interactive?: boolean;
277
524
  /** Test/embedding override for opening the approval URL. Defaults to the OS browser opener. */
278
525
  openApprovalUrl?: (url: string) => Promise<void>;
526
+ /** Explicit consent for a non-dry-run plan containing `prod` or `production`. */
279
527
  yes?: boolean;
280
528
  fetch?: typeof fetch;
529
+ /** Test/embedding override for Wrangler subprocesses used by `pushSecrets`. */
530
+ secretRunner?: CommandRunner;
281
531
  stdout?: Pick<typeof console, "log" | "error">;
282
532
  }
533
+ /** Human-reviewable, secret-free plan printed by `provision({ dryRun: true })`. */
283
534
  interface ProvisionPlan {
284
535
  appId: string;
285
536
  appName: string;
@@ -291,6 +542,7 @@ interface ProvisionPlan {
291
542
  hasRules: boolean;
292
543
  aiProvider?: string;
293
544
  }
545
+ /** Inputs for a read-only live environment smoke check. */
294
546
  interface SmokeOptions {
295
547
  configPath: string;
296
548
  env?: string;
@@ -298,21 +550,50 @@ interface SmokeOptions {
298
550
  stdout?: Pick<typeof console, "log" | "error">;
299
551
  }
300
552
 
553
+ /**
554
+ * Provision an app end-to-end from its config — the primary deploy path, and
555
+ * (apart from key rotation) idempotent, so re-running is safe. Obtains a
556
+ * developer token, creates the registry app when absent, enables each service
557
+ * per env (AI also gets `setAi` with the configured provider/model), configures
558
+ * Clerk auth and links, then per env mints or reuses credentials only for the
559
+ * configured services, pushes the schema with the db key and the
560
+ * rules with the developer token, and stores the provider key in the tenant
561
+ * vault when `ai.keyEnv` is set and present. Finally writes the credentials file
562
+ * (mode 0600, gitignored) and, when requested, `.dev.vars`.
563
+ *
564
+ * The developer token comes from (in order) `options.token`, `$ODLA_DEV_TOKEN`,
565
+ * the cached `.odla` token, or a fresh device handshake — which prints a user
566
+ * code + approval URL and, in interactive terminals, auto-opens the browser
567
+ * (`open: true`/`false` forces/suppresses it). Network error bodies pass through
568
+ * `redactSecrets` before display.
569
+ *
570
+ * @param options.dryRun Print the resolved plan and return before any network call/write.
571
+ * @param options.rotateKeys Rotate db keys and o11y tokens.
572
+ * @param options.rotateO11yToken Rotate only the o11y token.
573
+ * @param options.pushSecrets Preflight Wrangler, then push each env's configured
574
+ * Worker secrets immediately after credential provisioning.
575
+ * @param options.writeDevVars `true`/a path writes `.dev.vars`; `writeCredentials: false` skips the creds file.
576
+ * @param options.yes Required for a non-dry-run plan containing `prod` or
577
+ * `production`; callers must show and review `dryRun` output first.
578
+ * @throws When a production plan is executed without `yes`, configuration is
579
+ * invalid, authorization fails, or a provisioning operation fails.
580
+ */
301
581
  declare function provision(options: ProvisionOptions): Promise<void>;
302
582
 
583
+ /**
584
+ * Return `value` with every secret-shaped substring replaced by a `[redacted]`
585
+ * placeholder that keeps the identifying prefix (e.g. `sk_live_[redacted]`).
586
+ * Matches odla db keys (`odla_sk_`/`odla_dev_`), Clerk secret keys
587
+ * (`sk_live_`/`sk_test_`), OpenAI keys (`sk-`), webhook signing secrets
588
+ * (`whsec_`), o11y tokens (`o11y_`), GitHub tokens (`ghp_`/`gho_`/`github_pat_`),
589
+ * and AWS access-key ids (`AKIA…`). Clerk publishable keys (`pk_test_`/
590
+ * `pk_live_`) are public by design and intentionally left untouched. Used to
591
+ * sanitize anything the CLI echoes (notably network error bodies) before it
592
+ * reaches stdout/stderr. Non-matching input is returned unchanged.
593
+ */
303
594
  declare function redactSecrets(value: string): string;
304
595
 
305
- interface RunResult {
306
- code: number;
307
- stdout: string;
308
- stderr: string;
309
- }
310
- /** Subprocess seam: injectable in tests, `defaultRunner` in production. */
311
- type CommandRunner = (cmd: string, args: string[], opts?: {
312
- input?: string;
313
- cwd?: string;
314
- }) => Promise<RunResult>;
315
-
596
+ /** Moves locally stored configured service credentials to one Wrangler environment over stdin. */
316
597
  interface SecretsPushOptions {
317
598
  configPath: string;
318
599
  env: string;
@@ -323,38 +604,145 @@ interface SecretsPushOptions {
323
604
  stdout?: Pick<typeof console, "log" | "error">;
324
605
  }
325
606
  /**
326
- * Moves the env's odla-db key from `.odla/credentials.local.json` into the
327
- * Worker via `wrangler secret put ODLA_API_KEY`, piping the value over stdin
328
- * so it never appears on argv, in output, or in the conversation transcript.
607
+ * Moves the env's configured db and/or o11y credentials from the private local
608
+ * credentials file into the Worker, piping values over stdin so they never
609
+ * appear on argv, in output, or in the conversation transcript.
329
610
  */
330
611
  declare function secretsPush(options: SecretsPushOptions): Promise<void>;
331
612
 
613
+ /** Built-in security coverage profile selected for a hosted reasoning run. */
614
+ type HostedSecurityProfile = "odla" | "cloudflare-app" | "generic";
615
+ /** Exact attribution and authorization need supplied to a lazy token callback. */
616
+ interface HostedSecurityTokenRequest {
617
+ platform: string;
618
+ appId: string;
619
+ env: string;
620
+ selfAudit: boolean;
621
+ /** Self-audit asks the caller for an explicitly scoped platform credential. */
622
+ /** Ordinary app runs use owner authority and therefore request no platform scope. */
623
+ scope: "platform:security:self" | null;
624
+ }
625
+ /** Local snapshot/orchestration controls for an app-attributed hosted security
626
+ * run. The platform supplies model routes and provider credentials. */
627
+ interface RunHostedSecurityOptions {
628
+ /** Project config; loaded for customer-app runs and ignored for self-audit. */
629
+ configPath?: string;
630
+ /** Scan odla itself under the protected odla-ai/prod identity. */
631
+ selfAudit?: boolean;
632
+ /** Repository directory to snapshot. Defaults to the config root or cwd for self-audit. */
633
+ target?: string;
634
+ /** Private artifact directory. Defaults to `<target>/.odla/security/hosted`. */
635
+ out?: string;
636
+ /** Customer environment. Must be declared in config. Self-audit always uses prod. */
637
+ env?: string;
638
+ profile?: HostedSecurityProfile;
639
+ maxHuntTasks?: number;
640
+ /** Exact acknowledgement required before any source capture or network call. */
641
+ sourceDisclosureAck?: "redacted";
642
+ /** Optional client correlation id; the platform returns the authoritative run id. */
643
+ runId?: string;
644
+ /** Platform override. Customer runs otherwise use config.platformUrl. */
645
+ platform?: string;
646
+ /** Injected app credential; for self-audit this must be explicitly scoped by the caller. */
647
+ token?: string;
648
+ /** Lazy token acquisition. Receives the exact app/env and required scope. */
649
+ getToken?: (request: HostedSecurityTokenRequest) => string | Promise<string>;
650
+ fetch?: typeof fetch;
651
+ signal?: AbortSignal;
652
+ stdout?: Pick<typeof console, "log" | "error">;
653
+ }
654
+ /** Completed local report, public platform run metadata, and private artifact path. */
655
+ interface HostedSecurityResult {
656
+ report: SecurityReport;
657
+ run: Readonly<PlatformSecurityRun>;
658
+ output: string;
659
+ }
660
+ /**
661
+ * Run the hosted, redacted-source security harness for an odla app or odla's
662
+ * protected self-audit identity. Provider/model selection and provider secrets
663
+ * remain platform-owned; this function accepts only an app/scoped bearer and
664
+ * the opaque role grants remain inside @odla-ai/security's reasoner closures.
665
+ */
666
+ declare function runHostedSecurity(options: RunHostedSecurityOptions): Promise<HostedSecurityResult>;
667
+
668
+ /** Agent harnesses with a first-class local adapter. */
669
+ declare const AGENT_HARNESSES: readonly ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
670
+ /** A supported coding-agent harness. `agents` is the portable `AGENTS.md` adapter. */
671
+ type AgentHarness = (typeof AGENT_HARNESSES)[number];
672
+ /** A harness name accepted by setup; `all` expands to every project-local adapter. */
673
+ type AgentHarnessSelector = AgentHarness | "all";
674
+ /** Controls project-local or global installation of the bundled agent runbooks. */
332
675
  interface SkillInstallOptions {
333
- /** Project directory receiving `.claude/skills/`. Defaults to cwd. Ignored with `global`. */
676
+ /** Project directory receiving the shared runbooks and harness adapters. Defaults to cwd. */
334
677
  dir?: string;
335
- /** Install into `~/.claude/skills/` instead of the project. */
678
+ /** Install user-wide Claude and/or Codex skills instead of project adapters. */
336
679
  global?: boolean;
337
- /** Overwrite locally modified skill files. */
680
+ /** Harnesses to install. The programmatic compatibility default is `claude`; the CLI passes `all`. */
681
+ harnesses?: AgentHarnessSelector[];
682
+ /** Overwrite locally modified odla-managed files or managed instruction sections. */
338
683
  force?: boolean;
339
684
  /** Test/embedding override for the home directory. */
340
685
  homeDir?: string;
686
+ /** Test/embedding override for `$CODEX_HOME`. */
687
+ codexHomeDir?: string;
341
688
  /** Test/embedding override for the bundled skills directory. */
342
689
  sourceDir?: string;
343
690
  stdout?: Pick<typeof console, "log" | "error">;
344
691
  }
692
+ /** One harness adapter and the directories or files through which it discovers odla. */
693
+ interface HarnessInstallResult {
694
+ harness: AgentHarness;
695
+ targets: string[];
696
+ }
697
+ /** Files written or left unchanged by an idempotent agent-runbook installation. */
345
698
  interface SkillInstallResult {
699
+ /** Primary native skill directory, retained for compatibility with earlier callers. */
346
700
  targetDir: string;
701
+ /** Files changed directly under `targetDir`, relative to that directory. */
347
702
  written: string[];
703
+ /** Files already current directly under `targetDir`, relative to that directory. */
348
704
  unchanged: string[];
705
+ /** Absolute paths of every changed shared-runbook or adapter file. */
706
+ writtenPaths: string[];
707
+ /** Absolute paths of every already-current shared-runbook or adapter file. */
708
+ unchangedPaths: string[];
709
+ harnesses: HarnessInstallResult[];
349
710
  }
350
711
  /**
351
- * Copies the skills bundled with this package into `.claude/skills/` so the
352
- * user's agent picks them up. Idempotent: unchanged files are skipped, and a
353
- * locally modified file is never overwritten without `force`.
712
+ * Installs one shared offline runbook tree plus thin native adapters for the
713
+ * selected coding-agent harnesses. Project installs use `.agents/skills/` as
714
+ * the canonical bundle; Claude Code, Cursor, Copilot, Gemini, and generic
715
+ * `AGENTS.md` adapters point to it. Global installs support the native Claude
716
+ * and Codex skill roots only.
717
+ *
718
+ * Installation is idempotent and planned before any write: an odla-managed
719
+ * file changed by the user aborts the whole operation unless `force` is set.
720
+ * Existing `AGENTS.md`, `GEMINI.md`, and Copilot instructions are preserved;
721
+ * only the bounded odla-managed section is appended or replaced.
354
722
  */
355
723
  declare function installSkill(options?: SkillInstallOptions): SkillInstallResult;
356
724
 
725
+ /**
726
+ * Verify a live, already-provisioned environment end-to-end. Resolves the target
727
+ * env (`options.env`, else `dev`, else the first declared env) and requires it to
728
+ * be declared in the config. Reads the local credentials file and checks that it
729
+ * belongs to this app and holds a `tenantId` + `dbKey` for the env — otherwise it
730
+ * throws pointing the user at `odla-ai provision --write-dev-vars`.
731
+ *
732
+ * It then makes real network calls: fetches the registry public-config (and, when
733
+ * `ai.provider` is configured, asserts the live provider matches), fetches the
734
+ * live db schema with the db key and asserts every entity from the local schema
735
+ * is present, and runs a `count` aggregate against the first entity to confirm
736
+ * the db answers queries. Progress lines are printed and it ends with `ok`.
737
+ * Read-only — it writes nothing and throws on the first mismatch or failed
738
+ * request.
739
+ *
740
+ * @param options.configPath Path to the `odla.config.mjs`.
741
+ * @param options.env Env to check (default `dev`, else the first declared env).
742
+ * @param options.fetch Injectable `fetch` (for tests/embedding).
743
+ * @param options.stdout Optional console-like sink (defaults to `console`).
744
+ */
357
745
  declare function smoke(options: SmokeOptions): Promise<void>;
358
746
 
359
- export { type LoadedProjectConfig, type LocalCredentials, type OdlaProjectConfig, type ProvisionOptions, type ProvisionPlan, type SecretsPushOptions, type SkillInstallOptions, type SkillInstallResult, type SmokeOptions, doctor, initProject, installSkill, provision, redactSecrets, runCli, secretsPush, smoke };
747
+ export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, CAPABILITIES, type HarnessInstallResult, type HostedSecurityProfile, type HostedSecurityResult, type HostedSecurityTokenRequest, type LoadedProjectConfig, type LocalCredentials, type OdlaProjectConfig, type ProvisionOptions, type ProvisionPlan, type RunHostedSecurityOptions, SYSTEM_AI_PURPOSES, type ScopedPlatformTokenOptions, type SecretsPushOptions, type SkillInstallOptions, type SkillInstallResult, type SmokeOptions, type SystemAiPurpose, adminAi, doctor, getScopedPlatformToken, initProject, installSkill, printCapabilities, provision, redactSecrets, runCli, runHostedSecurity, secretsPush, smoke };
360
748
  ```