@odla-ai/cli 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -4
- package/dist/bin.cjs +1113 -257
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-643B2AKG.js → chunk-MJYQ7YDH.js} +1075 -195
- package/dist/chunk-MJYQ7YDH.js.map +1 -0
- package/dist/index.cjs +1156 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +188 -26
- package/dist/index.d.ts +188 -26
- package/dist/index.js +33 -3
- package/llms.txt +286 -33
- package/package.json +3 -3
- package/skills/odla/SKILL.md +8 -1
- package/skills/odla/references/build.md +17 -2
- package/skills/odla/references/sdks.md +2 -2
- package/skills/odla-migrate/references/phase-2-db.md +2 -2
- package/skills/odla-migrate/references/phase-3b-user-sync.md +15 -8
- package/skills/odla-migrate/references/phase-5-cutover.md +8 -2
- package/skills/odla-migrate/references/secrets-map.md +16 -2
- package/dist/chunk-643B2AKG.js.map +0 -1
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @odla-ai/cli — LLM context
|
|
2
2
|
|
|
3
|
-
> Agent-operable CLI for odla provisioning,
|
|
3
|
+
> Agent-operable CLI for odla provisioning, System AI administration, commit-pinned GitHub security jobs, Worker secrets, and smoke checks.
|
|
4
4
|
|
|
5
5
|
> ⚠️ **Early access — pre-1.0.** Agents work from bounded runbooks; humans
|
|
6
6
|
> approve credentials, production changes, releases, and merges. APIs and exact
|
|
@@ -31,8 +31,8 @@ For a project you keep working in, install it as a dev dependency so the
|
|
|
31
31
|
shorter `npx odla-ai` form works and the version is pinned by your lockfile:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm view @odla-ai/cli@0.
|
|
35
|
-
npm i -D --save-exact @odla-ai/cli@0.
|
|
34
|
+
npm view @odla-ai/cli@0.9.0 version
|
|
35
|
+
npm i -D --save-exact @odla-ai/cli@0.9.0
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Prerequisites
|
|
@@ -61,7 +61,15 @@ npx odla-ai provision --dry-run
|
|
|
61
61
|
npx odla-ai provision --write-dev-vars --push-secrets
|
|
62
62
|
npx odla-ai smoke --env dev
|
|
63
63
|
npx odla-ai secrets push --env dev
|
|
64
|
-
npx odla-ai
|
|
64
|
+
npx odla-ai secrets set clerk_webhook_secret --env dev --stdin
|
|
65
|
+
npx odla-ai secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY
|
|
66
|
+
npx @odla-ai/cli security run . --env dev --ack-redacted-source
|
|
67
|
+
npx @odla-ai/cli security github connect --env dev # infers owner/name from git origin
|
|
68
|
+
npx @odla-ai/cli security plan --env dev
|
|
69
|
+
npx @odla-ai/cli security sources --env dev
|
|
70
|
+
npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
|
|
71
|
+
npx @odla-ai/cli security status <job-id>
|
|
72
|
+
npx @odla-ai/cli security report <job-id>
|
|
65
73
|
npx odla-ai admin ai show
|
|
66
74
|
npx odla-ai admin ai models
|
|
67
75
|
npx odla-ai admin ai set security --discovery-provider anthropic --discovery-model claude-opus-4-8 --validation-provider openai --validation-model gpt-5.5 --enabled
|
|
@@ -129,7 +137,7 @@ when provisioning has not written `.odla/credentials.local.json`.
|
|
|
129
137
|
|
|
130
138
|
The bundled build/migration skills add a passive pre-ship gate with
|
|
131
139
|
`@odla-ai/security`: after the exact-version availability check, run
|
|
132
|
-
`npm i -D --save-exact @odla-ai/security@0.2.
|
|
140
|
+
`npm i -D --save-exact @odla-ai/security@0.2.2`, then
|
|
133
141
|
`npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical`.
|
|
134
142
|
The passive scan remains a separate binary: it makes no model calls and does
|
|
135
143
|
not execute target code. After explicit redacted-source approval,
|
|
@@ -155,12 +163,79 @@ carries a bounded `Retry-After` only when the upstream supplied one. The CLI
|
|
|
155
163
|
does not echo arbitrary response text or silently change the admin-selected
|
|
156
164
|
model.
|
|
157
165
|
Before installing the exact release, require
|
|
158
|
-
`npm view @odla-ai/security@0.2.
|
|
166
|
+
`npm view @odla-ai/security@0.2.2 version` to succeed before installation. An
|
|
159
167
|
exact-version `E404` means the release is unavailable, not that the security
|
|
160
168
|
preflight passed, and does not prove the package name is absent. odla's own
|
|
161
169
|
engineering environment uses an equivalent internal gate; customer projects
|
|
162
170
|
should use the published `odla-security` command above.
|
|
163
171
|
|
|
172
|
+
For a server-side, commit-pinned review, connect one repository through odla's
|
|
173
|
+
GitHub App. Contents and Metadata access are read-only; optional Checks write
|
|
174
|
+
access publishes only a best-effort summary. The CLI infers `owner/name` from a
|
|
175
|
+
safe GitHub HTTPS or SSH `origin`; pass `--repo owner/name` when it cannot. It
|
|
176
|
+
opens GitHub's installation/authorization page and polls that exact odla
|
|
177
|
+
attempt. Finish each browser stage within its displayed 15-minute window:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npx @odla-ai/cli security github connect --env dev
|
|
181
|
+
npx @odla-ai/cli security plan --env dev
|
|
182
|
+
npx @odla-ai/cli security sources --env dev
|
|
183
|
+
npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The source job follows by default, renders the normalized report, and applies
|
|
187
|
+
the same confirmed/candidate/incomplete gates; `--no-follow` only enqueues.
|
|
188
|
+
`security status` and `security report` inspect prior jobs, while `security
|
|
189
|
+
github disconnect --source <source-id> --env dev --yes` revokes the saved
|
|
190
|
+
source without requiring a GitHub-side uninstall. A source id belongs to one
|
|
191
|
+
app/environment; reconnect separately for another environment. Disconnect
|
|
192
|
+
cancels nonterminal work, while terminal reports remain until normal expiry.
|
|
193
|
+
The App is not subscribed to Push; an unexpectedly delivered Push event is
|
|
194
|
+
ignored and never starts a job. Source jobs default to `cloudflare-app`;
|
|
195
|
+
local-path jobs default to `odla`.
|
|
196
|
+
|
|
197
|
+
Clients send only an opaque odla source id, ref, profile, and explicit redacted
|
|
198
|
+
disclosure acknowledgement—never a PAT, installation id, provider key, or
|
|
199
|
+
provider/model selection override. The CLI deliberately displays the read-only
|
|
200
|
+
route identities in the consent preflight. odla.ai verifies access, pins the ref to a commit SHA,
|
|
201
|
+
fetches the bounded snapshot server-side, and uses the admin-selected System AI
|
|
202
|
+
routes. `security plan` shows both provider/model routes, exact policy
|
|
203
|
+
revisions and bounds, prompt/redaction/report contracts, readiness, retention,
|
|
204
|
+
and the no-execution boundary before consent. Each job requires
|
|
205
|
+
`--plan-digest` and binds acknowledgement to that complete digest; any covered
|
|
206
|
+
change requires a refreshed plan and acknowledgement.
|
|
207
|
+
|
|
208
|
+
After validating that plan digest, the CLI fetches and prints the atomic
|
|
209
|
+
server-authored execution intent for the exact app/environment, opaque source,
|
|
210
|
+
ref (or default branch), profile, redacted disclosure, and plan digest. Its
|
|
211
|
+
`executionDigest` is persisted with the job and sent back as
|
|
212
|
+
`expectedExecutionDigest`; a changed selection fails with `intent_conflict`
|
|
213
|
+
instead of running under generic consent. Repository display text is not digest
|
|
214
|
+
authority.
|
|
215
|
+
An omitted `--ref` is canonicalized to the source's current default-branch name
|
|
216
|
+
during preview. A branch-default change before enqueue invalidates the intent
|
|
217
|
+
instead of changing what the Workflow will acquire.
|
|
218
|
+
|
|
219
|
+
GitHub read access is not disclosure consent. No raw archive, file body, or
|
|
220
|
+
explicit source excerpt is deliberately persisted in the hosted report or
|
|
221
|
+
Workflow checkpoints. The private report may keep bounded,
|
|
222
|
+
best-effort credential-pattern-redacted model-derived prose and repository-relative paths for up to
|
|
223
|
+
90 days; treat it as sensitive because it may reveal source semantics.
|
|
224
|
+
Provider/GitHub terms are separate.
|
|
225
|
+
|
|
226
|
+
The archive envelope is 16 MiB compressed, 48 MiB decompressed, 20,000 entries,
|
|
227
|
+
10,000 files, 2 MiB per file, and 16 MiB aggregate file bytes. Links, special
|
|
228
|
+
entries, unsafe redirects, duplicate paths, and escaping paths are rejected.
|
|
229
|
+
The combined acquire/analyze step has zero automatic retries; after a terminal
|
|
230
|
+
failure, correct the ref/access/archive shape or wait for service recovery and
|
|
231
|
+
explicitly start a new job.
|
|
232
|
+
|
|
233
|
+
Checks are optional, best-effort summaries, use fixed conclusion semantics, and
|
|
234
|
+
are not the configurable CLI gate. Target code is not executed. Without an
|
|
235
|
+
injected isolated executor, hosted inference cannot mechanically confirm a
|
|
236
|
+
finding; critical candidates and incomplete coverage remain the operative
|
|
237
|
+
gates, and a clean result is not proof of security.
|
|
238
|
+
|
|
164
239
|
`admin ai show|models|set|credentials|credential set|usage|audit` manages platform-funded System
|
|
165
240
|
AI (`o11y.triage`, `security.discovery`, `security.validation`). It uses a
|
|
166
241
|
separate, mode-`0600` `.odla/admin-token.local.json` cache. Studio displays the
|
|
@@ -195,6 +270,22 @@ redacted plan without spawning anything. For the normal path, use
|
|
|
195
270
|
`provision --write-dev-vars --push-secrets` so service enablement, credential
|
|
196
271
|
issuance, local config, and deployed secret transfer stay one composition.
|
|
197
272
|
|
|
273
|
+
`secrets set <name> --env <env>` stores one named secret in that env's tenant
|
|
274
|
+
vault — the same write-only slot Studio's Secrets panel fills (for example
|
|
275
|
+
`clerk_webhook_secret` for synced auth). The value may come only from
|
|
276
|
+
`--from-env <NAME>` or `--stdin`, so a producer command pipes straight into the
|
|
277
|
+
vault without the secret ever appearing on argv, in output, or in an agent's
|
|
278
|
+
transcript; it is encrypted at rest and readable back only by that tenant's
|
|
279
|
+
app API key, never by the CLI, Studio, or the developer token. `$`-prefixed
|
|
280
|
+
names are platform-reserved. `secrets set-clerk-key --env <env>` stores the
|
|
281
|
+
app's Clerk secret key in the reserved `$clerk_secret` slot so the platform can
|
|
282
|
+
resolve the app's users (invites, member lookups); reserved secrets are never
|
|
283
|
+
readable by app keys. It refuses an `sk_test_` key for a prod-named env and
|
|
284
|
+
requires `--yes` to put an `sk_live_` key into a non-prod env, since live users
|
|
285
|
+
would sync into a non-prod tenant. Both commands authenticate with the
|
|
286
|
+
developer token (`$ODLA_DEV_TOKEN`, the cached token, or a fresh device
|
|
287
|
+
approval), and prod-named envs require `--yes`, matching `secrets push`.
|
|
288
|
+
|
|
198
289
|
## Who does what
|
|
199
290
|
|
|
200
291
|
If a change is deterministic from `odla.config.mjs`, the CLI owns it: app and
|
|
@@ -284,37 +375,34 @@ If schema is present and rules are omitted, `defaultRules: "deny"` generates
|
|
|
284
375
|
deny-all rules for every schema entity. That is the safe default for Workers
|
|
285
376
|
that mediate reads and writes with an app key.
|
|
286
377
|
|
|
287
|
-
## API reference (generated from dist/index.d.ts, v0.
|
|
378
|
+
## API reference (generated from dist/index.d.ts, v0.9.0)
|
|
288
379
|
|
|
289
380
|
```ts
|
|
290
381
|
import { SecurityReport, PlatformSecurityRun } from '@odla-ai/security';
|
|
382
|
+
import { HostedSecurityJob, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityIntentResponse, HostedSecurityPlan, HostedSecurityReport, HostedSecurityJobStatus } from '@odla-ai/security/hosted';
|
|
383
|
+
export { GitHubConnectionAttempt, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityCounts, HostedSecurityCoverage, HostedSecurityIntent, HostedSecurityIntentResponse, HostedSecurityJob, HostedSecurityJobStatus, HostedSecurityPlan, HostedSecurityPlanRoute, HostedSecurityReport, HostedSecurityReportFinding, HostedSecurityRoute } from '@odla-ai/security/hosted';
|
|
291
384
|
|
|
385
|
+
/** Injectable side-effect boundaries used to run and test the command dispatcher. */
|
|
386
|
+
interface CliDependencies {
|
|
387
|
+
fetch?: typeof fetch;
|
|
388
|
+
openUrl?: (url: string) => Promise<void>;
|
|
389
|
+
pollWait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
|
|
390
|
+
readGitOrigin?: (cwd: string) => Promise<string>;
|
|
391
|
+
confirm?: (message: string) => Promise<boolean>;
|
|
392
|
+
stdout?: Pick<typeof console, "log" | "error">;
|
|
393
|
+
}
|
|
292
394
|
/**
|
|
293
|
-
* Parse CLI arguments and dispatch
|
|
294
|
-
* `bin.ts` invokes. `argv` defaults to `process.argv.slice(2)`; the first
|
|
295
|
-
* positional selects the command and the rest are parsed into flags (supporting
|
|
296
|
-
* `--flag value`, `--flag=value`, boolean `--flag`, `--no-flag`, and repeated
|
|
297
|
-
* flags collected into arrays).
|
|
395
|
+
* Parse CLI arguments and dispatch the matching odla command.
|
|
298
396
|
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* transfers write-only credentials, and reads metadata-only attributed usage
|
|
304
|
-
* through separate short-lived exact-scope device grants;
|
|
305
|
-
* `security run` performs an explicitly acknowledged, app-attributed hosted
|
|
306
|
-
* reasoning pass; `provision` runs the full provision (including optional secret
|
|
307
|
-
* rotation, local vars, and deployed secret push); `smoke` checks a live env;
|
|
308
|
-
* `setup` and `skill install` install the bundled offline runbooks for Claude
|
|
309
|
-
* Code, Codex, Cursor, Copilot, Gemini, and `AGENTS.md`-aware agents; and
|
|
310
|
-
* `secrets push` pipes the env's configured db/o11y secrets into the Worker via
|
|
311
|
-
* Wrangler (requires `--env`). Throws on an unknown command/subcommand, unsupported option, extra
|
|
312
|
-
* positional argument, missing required value, or a dispatched command error.
|
|
397
|
+
* The dispatcher covers project setup, platform AI administration, hosted and
|
|
398
|
+
* local security, provisioning, smoke checks, skill installation, and secret
|
|
399
|
+
* pushes. It rejects unknown commands, unsupported options, extra positionals,
|
|
400
|
+
* missing values, and errors returned by the selected command.
|
|
313
401
|
*
|
|
314
|
-
* @param argv Argument vector without the node/script prefix
|
|
315
|
-
*
|
|
402
|
+
* @param argv Argument vector without the node/script prefix.
|
|
403
|
+
* @param dependencies Optional side-effect overrides for embedding and tests.
|
|
316
404
|
*/
|
|
317
|
-
declare function runCli(argv?: string[]): Promise<void>;
|
|
405
|
+
declare function runCli(argv?: string[], dependencies?: CliDependencies): Promise<void>;
|
|
318
406
|
|
|
319
407
|
/** One exact platform scope obtainable through the admin device flow. */
|
|
320
408
|
type ScopedPlatformScope = "platform:ai:policy:read" | "platform:ai:policy:write" | "platform:ai:credential:read" | "platform:ai:credential:write" | "platform:ai:usage:read" | "platform:ai:audit:read" | "platform:security:self";
|
|
@@ -383,10 +471,10 @@ declare function adminAi(options: AdminAiOptions): Promise<void>;
|
|
|
383
471
|
/** Stable, machine-readable division of work between the CLI, coding agent,
|
|
384
472
|
* human operator, and Studio. Printed by `odla-ai capabilities --json`. */
|
|
385
473
|
declare const CAPABILITIES: {
|
|
386
|
-
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 policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"];
|
|
474
|
+
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", "store tenant-vault secrets (including the Clerk webhook secret and reserved Clerk secret key) write-only from stdin or an env var", "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", "connect/revoke source-read-only GitHub sources and drive commit-pinned hosted security jobs without PATs or provider keys", "let admins manage system AI policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"];
|
|
387
475
|
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"];
|
|
388
|
-
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"];
|
|
389
|
-
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"];
|
|
476
|
+
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", "approve GitHub App repository access separately from redacted-snippet disclosure"];
|
|
477
|
+
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", "connect/revoke GitHub security sources and inspect ref-to-SHA jobs, routes, retention, coverage, and normalized reports"];
|
|
390
478
|
};
|
|
391
479
|
type Output = Pick<typeof console, "log">;
|
|
392
480
|
/** Print the stable responsibility boundary for humans or agent tooling. */
|
|
@@ -659,6 +747,47 @@ interface SecretsPushOptions {
|
|
|
659
747
|
*/
|
|
660
748
|
declare function secretsPush(options: SecretsPushOptions): Promise<void>;
|
|
661
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Inputs for one write-only tenant-vault transfer (`secrets set` /
|
|
752
|
+
* `secrets set-clerk-key`). Values may come only from a named environment
|
|
753
|
+
* variable or stdin, so a producer command can be piped straight into the
|
|
754
|
+
* vault without the secret ever being displayed or stored on disk.
|
|
755
|
+
*/
|
|
756
|
+
interface SecretsSetOptions {
|
|
757
|
+
configPath: string;
|
|
758
|
+
env: string;
|
|
759
|
+
/** Vault secret name (`secrets set` only; `$`-prefixed names are reserved). */
|
|
760
|
+
name?: string;
|
|
761
|
+
fromEnv?: string;
|
|
762
|
+
stdin?: boolean;
|
|
763
|
+
/** Explicit consent for a prod-named env (and for sk_live_ into a non-prod env). */
|
|
764
|
+
yes?: boolean;
|
|
765
|
+
/** Developer token override; defaults to $ODLA_DEV_TOKEN, the cached token, or a device handshake. */
|
|
766
|
+
token?: string;
|
|
767
|
+
open?: boolean;
|
|
768
|
+
interactive?: boolean;
|
|
769
|
+
openApprovalUrl?: (url: string) => Promise<void>;
|
|
770
|
+
fetch?: typeof fetch;
|
|
771
|
+
readStdin?: () => Promise<string>;
|
|
772
|
+
stdout?: Pick<typeof console, "log" | "error">;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Store one named secret in the env's tenant vault (the same write-only slot
|
|
776
|
+
* Studio's Secrets panel fills), e.g. `clerk_webhook_secret` for synced auth.
|
|
777
|
+
* The value is encrypted at rest and can be read back only by that tenant's
|
|
778
|
+
* app API key — never by this CLI, Studio, or the developer token.
|
|
779
|
+
*/
|
|
780
|
+
declare function secretsSet(options: SecretsSetOptions): Promise<void>;
|
|
781
|
+
/**
|
|
782
|
+
* Store the app's Clerk secret key under the reserved `$clerk_secret` slot so
|
|
783
|
+
* odla-db can resolve the app's users (invites, member lookups). Reserved
|
|
784
|
+
* secrets are never readable by app keys; the dedicated endpoint is the only
|
|
785
|
+
* way to set one. Guards against instance/env mismatches: an sk_test_ key
|
|
786
|
+
* never belongs in a prod-named env, and an sk_live_ key entering a non-prod
|
|
787
|
+
* env requires `--yes` (live users would sync into a non-prod tenant).
|
|
788
|
+
*/
|
|
789
|
+
declare function secretsSetClerkKey(options: SecretsSetOptions): Promise<void>;
|
|
790
|
+
|
|
662
791
|
/** Built-in security coverage profile selected for a hosted reasoning run. */
|
|
663
792
|
type HostedSecurityProfile = "odla" | "cloudflare-app" | "generic";
|
|
664
793
|
/** Exact attribution and authorization need supplied to a lazy token callback. */
|
|
@@ -714,6 +843,130 @@ interface HostedSecurityResult {
|
|
|
714
843
|
*/
|
|
715
844
|
declare function runHostedSecurity(options: RunHostedSecurityOptions): Promise<HostedSecurityResult>;
|
|
716
845
|
|
|
846
|
+
/**
|
|
847
|
+
* The hosted-security client surface. The WIRE types live in @odla-ai/security/hosted — one
|
|
848
|
+
* definition shared with the dashboard and the server that produces them. Re-exported here so
|
|
849
|
+
* this module stays the single import path for the rest of the CLI.
|
|
850
|
+
*
|
|
851
|
+
* What remains below is what is genuinely the CLI's own: transport controls (platform origin,
|
|
852
|
+
* token, fetch, AbortSignal) that no other client shares.
|
|
853
|
+
*/
|
|
854
|
+
|
|
855
|
+
/** Common authenticated transport controls for hosted-security requests. */
|
|
856
|
+
interface HostedSecurityRequestOptions {
|
|
857
|
+
platform: string;
|
|
858
|
+
token: string;
|
|
859
|
+
fetch?: typeof fetch;
|
|
860
|
+
signal?: AbortSignal;
|
|
861
|
+
}
|
|
862
|
+
/** Options for browser-mediated connection of one exact GitHub repository. */
|
|
863
|
+
interface ConnectGitHubSecuritySourceOptions extends HostedSecurityRequestOptions {
|
|
864
|
+
appId: string;
|
|
865
|
+
env: string;
|
|
866
|
+
/** Exact owner/name repository requested before GitHub approval. */
|
|
867
|
+
repository: string;
|
|
868
|
+
/** false prints but does not launch the install URL. Defaults to true. */
|
|
869
|
+
open?: boolean;
|
|
870
|
+
openInstallUrl?: (url: string) => Promise<void>;
|
|
871
|
+
/** Poll interval. The server attempt expiry remains authoritative. */
|
|
872
|
+
pollIntervalMs?: number;
|
|
873
|
+
pollTimeoutMs?: number;
|
|
874
|
+
wait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
|
|
875
|
+
now?: () => number;
|
|
876
|
+
stdout?: Pick<typeof console, "log">;
|
|
877
|
+
}
|
|
878
|
+
/** Options for listing GitHub sources or reading the plan for one app environment. */
|
|
879
|
+
interface ListGitHubSecuritySourcesOptions extends HostedSecurityRequestOptions {
|
|
880
|
+
appId: string;
|
|
881
|
+
env: string;
|
|
882
|
+
}
|
|
883
|
+
/** Inputs for fetching the server-authored execution preview before enqueue. */
|
|
884
|
+
interface GetHostedSecurityIntentOptions extends HostedSecurityRequestOptions {
|
|
885
|
+
appId: string;
|
|
886
|
+
env: string;
|
|
887
|
+
sourceId: string;
|
|
888
|
+
ref?: string;
|
|
889
|
+
profile?: "odla" | "cloudflare-app" | "generic";
|
|
890
|
+
}
|
|
891
|
+
/** Options for revoking odla access to one saved GitHub source. */
|
|
892
|
+
interface DisconnectGitHubSecuritySourceOptions extends HostedSecurityRequestOptions {
|
|
893
|
+
appId: string;
|
|
894
|
+
sourceId: string;
|
|
895
|
+
}
|
|
896
|
+
/** Options for enqueueing a commit-pinned hosted security review. */
|
|
897
|
+
interface StartHostedSecurityJobOptions extends HostedSecurityRequestOptions {
|
|
898
|
+
appId: string;
|
|
899
|
+
env: string;
|
|
900
|
+
sourceId: string;
|
|
901
|
+
ref?: string;
|
|
902
|
+
profile?: "odla" | "cloudflare-app" | "generic";
|
|
903
|
+
clientRequestId?: string;
|
|
904
|
+
/** Versions shown in the disclosure preflight. The server rejects stale consent. */
|
|
905
|
+
expectedPolicyVersions: {
|
|
906
|
+
discovery: number;
|
|
907
|
+
validation: number;
|
|
908
|
+
};
|
|
909
|
+
/** Digest copied from the plan the app owner explicitly reviewed. */
|
|
910
|
+
expectedPlanDigest: `sha256:${string}`;
|
|
911
|
+
/** Digest copied from the exact server-authored source/ref/profile preview. */
|
|
912
|
+
expectedExecutionDigest: `sha256:${string}`;
|
|
913
|
+
/** Explicit disclosure acknowledgement; GitHub read access alone is not consent. */
|
|
914
|
+
sourceDisclosureAck?: "redacted";
|
|
915
|
+
}
|
|
916
|
+
/** Options for reading one hosted security job or its report. */
|
|
917
|
+
interface GetHostedSecurityJobOptions extends HostedSecurityRequestOptions {
|
|
918
|
+
appId: string;
|
|
919
|
+
jobId: string;
|
|
920
|
+
}
|
|
921
|
+
/** Options for listing hosted security jobs in one app environment. */
|
|
922
|
+
interface ListHostedSecurityJobsOptions extends HostedSecurityRequestOptions {
|
|
923
|
+
appId: string;
|
|
924
|
+
env: string;
|
|
925
|
+
}
|
|
926
|
+
/** Polling controls for following one hosted job to a terminal state. */
|
|
927
|
+
interface FollowHostedSecurityJobOptions extends GetHostedSecurityJobOptions {
|
|
928
|
+
pollIntervalMs?: number;
|
|
929
|
+
pollTimeoutMs?: number;
|
|
930
|
+
wait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
|
|
931
|
+
now?: () => number;
|
|
932
|
+
onUpdate?: (job: Readonly<HostedSecurityJob>) => void;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/** Begin GitHub App installation, launch its server-issued GitHub URL, and
|
|
936
|
+
* poll the exact attempt until the callback has connected a source. */
|
|
937
|
+
declare function connectGitHubSecuritySource(options: ConnectGitHubSecuritySourceOptions): Promise<GitHubConnectionStatus>;
|
|
938
|
+
/** List repository sources the current app owner connected for one environment. */
|
|
939
|
+
declare function listGitHubSecuritySources(options: ListGitHubSecuritySourcesOptions): Promise<GitHubSecuritySource[]>;
|
|
940
|
+
/** Revoke odla's use of one saved source without requiring a GitHub-side App
|
|
941
|
+
* uninstall. The server marks the source deleted and rejects new jobs. */
|
|
942
|
+
declare function disconnectGitHubSecuritySource(options: DisconnectGitHubSecuritySourceOptions): Promise<void>;
|
|
943
|
+
/** Parse an HTTPS, ssh://, or git@github.com origin without retaining embedded
|
|
944
|
+
* credentials. Only github.com owner/name remotes are accepted. */
|
|
945
|
+
declare function repositoryFromGitRemote(remoteInput: string): string;
|
|
946
|
+
/** Read the current repository's origin without invoking a shell. */
|
|
947
|
+
declare function inferGitHubRepository(cwd?: string, readOrigin?: (cwd: string) => Promise<string>): Promise<string>;
|
|
948
|
+
|
|
949
|
+
/** Fetch the atomic processing-plan and source/ref/profile execution preview. */
|
|
950
|
+
declare function getHostedSecurityIntent(options: GetHostedSecurityIntentOptions): Promise<HostedSecurityIntentResponse>;
|
|
951
|
+
|
|
952
|
+
/** Read the admin-selected routes, budgets, readiness, disclosure boundary,
|
|
953
|
+
* and digest that must be acknowledged when a hosted review is enqueued. */
|
|
954
|
+
declare function getHostedSecurityPlan(options: ListGitHubSecuritySourcesOptions): Promise<HostedSecurityPlan>;
|
|
955
|
+
/** Enqueue a commit-pinned server-side review. The server resolves sourceId to
|
|
956
|
+
* its saved GitHub App installation; clients cannot choose an installation or
|
|
957
|
+
* pass a PAT. */
|
|
958
|
+
declare function startHostedSecurityJob(options: StartHostedSecurityJobOptions): Promise<HostedSecurityJob>;
|
|
959
|
+
/** Read one bounded hosted job status without an archive or explicit source excerpt. */
|
|
960
|
+
declare function getHostedSecurityJob(options: GetHostedSecurityJobOptions): Promise<HostedSecurityJob>;
|
|
961
|
+
/** List bounded, newest-first job metadata for an app environment. */
|
|
962
|
+
declare function listHostedSecurityJobs(options: ListHostedSecurityJobsOptions): Promise<HostedSecurityJob[]>;
|
|
963
|
+
/** Poll one exact job until it reaches a terminal state. */
|
|
964
|
+
declare function followHostedSecurityJob(options: FollowHostedSecurityJobOptions): Promise<HostedSecurityJob>;
|
|
965
|
+
/** Fetch the bounded normalized report projection for a completed hosted job. */
|
|
966
|
+
declare function getHostedSecurityReport(options: GetHostedSecurityJobOptions): Promise<HostedSecurityReport>;
|
|
967
|
+
/** Return whether a hosted job status can no longer transition. */
|
|
968
|
+
declare function isTerminalHostedSecurityStatus(status: HostedSecurityJobStatus): boolean;
|
|
969
|
+
|
|
717
970
|
/** Agent harnesses with a first-class local adapter. */
|
|
718
971
|
declare const AGENT_HARNESSES: readonly ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
|
|
719
972
|
/** A supported coding-agent harness. `agents` is the portable `AGENTS.md` adapter. */
|
|
@@ -793,5 +1046,5 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
|
|
|
793
1046
|
*/
|
|
794
1047
|
declare function smoke(options: SmokeOptions): Promise<void>;
|
|
795
1048
|
|
|
796
|
-
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 };
|
|
1049
|
+
export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, CAPABILITIES, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type HarnessInstallResult, type HostedSecurityProfile, type HostedSecurityResult, type HostedSecurityTokenRequest, type ListGitHubSecuritySourcesOptions, type ListHostedSecurityJobsOptions, type LoadedProjectConfig, type LocalCredentials, type OdlaProjectConfig, type ProvisionOptions, type ProvisionPlan, type RunHostedSecurityOptions, SYSTEM_AI_PURPOSES, type ScopedPlatformTokenOptions, type SecretsPushOptions, type SecretsSetOptions, type SkillInstallOptions, type SkillInstallResult, type SmokeOptions, type StartHostedSecurityJobOptions, type SystemAiPurpose, adminAi, connectGitHubSecuritySource, disconnectGitHubSecuritySource, doctor, followHostedSecurityJob, getHostedSecurityIntent, getHostedSecurityJob, getHostedSecurityPlan, getHostedSecurityReport, getScopedPlatformToken, inferGitHubRepository, initProject, installSkill, isTerminalHostedSecurityStatus, listGitHubSecuritySources, listHostedSecurityJobs, printCapabilities, provision, redactSecrets, repositoryFromGitRemote, runCli, runHostedSecurity, secretsPush, secretsSet, secretsSetClerkKey, smoke, startHostedSecurityJob };
|
|
797
1050
|
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odla-ai/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agent-operable CLI for odla provisioning,
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Agent-operable CLI for odla provisioning, System AI administration, commit-pinned GitHub security jobs, Worker secrets, and smoke checks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://odla.ai/docs/packages/cli",
|
|
7
7
|
"repository": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@odla-ai/ai": "^0.3.3",
|
|
63
63
|
"@odla-ai/apps": "^0.7.1",
|
|
64
64
|
"@odla-ai/db": "^0.6.2",
|
|
65
|
-
"@odla-ai/security": "^0.2.
|
|
65
|
+
"@odla-ai/security": "^0.2.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/node": "^22.10.0",
|
package/skills/odla/SKILL.md
CHANGED
|
@@ -74,7 +74,14 @@ guessing which platform/credential steps need manual work. Then:
|
|
|
74
74
|
redacted source disclosure, follow with
|
|
75
75
|
`npx @odla-ai/cli security run . --env dev --ack-redacted-source` for
|
|
76
76
|
app-attributed discovery + independent validation. Never request provider
|
|
77
|
-
keys; the platform selects the routes and returns bounded role grants.
|
|
77
|
+
keys; the platform selects the routes and returns bounded role grants. For
|
|
78
|
+
repeatable server-side review, run `security github connect --env dev`
|
|
79
|
+
(human approves source-read-only GitHub access), `security plan --env dev`,
|
|
80
|
+
`security sources --env dev`, then `security run --source <id> --ref <ref>
|
|
81
|
+
--env dev --plan-digest <digest-from-security-plan>
|
|
82
|
+
--ack-redacted-source`.
|
|
83
|
+
The source job follows, reports, and gates by default. Never request a PAT or
|
|
84
|
+
treat GitHub read approval as redacted-snippet disclosure consent.
|
|
78
85
|
6. **ship** ⏸ — after adding `prod`, run `npx @odla-ai/cli provision --dry-run`,
|
|
79
86
|
show the human, then `npx @odla-ai/cli provision --yes --push-secrets` and
|
|
80
87
|
`npx wrangler deploy`.
|
|
@@ -66,12 +66,12 @@ re-provision to push it, and retry.
|
|
|
66
66
|
Install the passive harness and scan before any production secret or deploy:
|
|
67
67
|
|
|
68
68
|
Before installing the exact release, first verify
|
|
69
|
-
`npm view @odla-ai/security@0.2.
|
|
69
|
+
`npm view @odla-ai/security@0.2.2 version`. An exact-version `E404` means the
|
|
70
70
|
release is unavailable and blocks this preflight; it is not a clean scan and
|
|
71
71
|
does not prove the package name is absent.
|
|
72
72
|
|
|
73
73
|
```
|
|
74
|
-
npm i -D --save-exact @odla-ai/security@0.2.
|
|
74
|
+
npm i -D --save-exact @odla-ai/security@0.2.2
|
|
75
75
|
npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical
|
|
76
76
|
```
|
|
77
77
|
|
|
@@ -87,6 +87,21 @@ disclosure under the configured providers' retention/residency terms):
|
|
|
87
87
|
npx @odla-ai/cli security run . --env dev --ack-redacted-source
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
For a repeatable commit-pinned job after the human approves the read-only
|
|
91
|
+
GitHub App:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
npx @odla-ai/cli security github connect --env dev
|
|
95
|
+
npx @odla-ai/cli security plan --env dev
|
|
96
|
+
npx @odla-ai/cli security sources --env dev
|
|
97
|
+
npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The CLI infers the repository from a safe GitHub origin, never requests a PAT
|
|
101
|
+
or provider key, follows the job and applies the gate by default. GitHub read
|
|
102
|
+
approval does not replace the explicit redacted-source acknowledgement. Target
|
|
103
|
+
code is not executed and a clean report is not proof of security.
|
|
104
|
+
|
|
90
105
|
Do not ask for Anthropic/OpenAI/Google keys. The CLI obtains app-owner auth;
|
|
91
106
|
the platform fixes separate discovery/validation grants to the admin-configured
|
|
92
107
|
routes, attributes usage to this app/env/run, and retains no source or prompts
|
|
@@ -70,12 +70,12 @@ Run the passive CLI in local/CI development; do not import it into the running
|
|
|
70
70
|
Worker:
|
|
71
71
|
|
|
72
72
|
Before installing the exact release, require
|
|
73
|
-
`npm view @odla-ai/security@0.2.
|
|
73
|
+
`npm view @odla-ai/security@0.2.2 version` to succeed before installing it. An
|
|
74
74
|
exact-version `E404` means the release is unavailable, not that the preflight
|
|
75
75
|
passed, and does not prove the package name is absent.
|
|
76
76
|
|
|
77
77
|
```
|
|
78
|
-
npm i -D --save-exact @odla-ai/security@0.2.
|
|
78
|
+
npm i -D --save-exact @odla-ai/security@0.2.2
|
|
79
79
|
npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical
|
|
80
80
|
```
|
|
81
81
|
|
|
@@ -10,8 +10,8 @@ in the browser in interactive terminals).
|
|
|
10
10
|
|
|
11
11
|
## Steps
|
|
12
12
|
|
|
13
|
-
1. Require `npm view @odla-ai/cli@0.
|
|
14
|
-
`npm i -D --save-exact @odla-ai/cli@0.
|
|
13
|
+
1. Require `npm view @odla-ai/cli@0.8.0 version` to succeed, then run
|
|
14
|
+
`npm i -D --save-exact @odla-ai/cli@0.8.0` and `npm i @odla-ai/db`.
|
|
15
15
|
2. `npx @odla-ai/cli init --app-id <id> --name "<Name>" --env dev --services db`
|
|
16
16
|
Review `odla.config.mjs`. Keep `envs: ["dev"]` — prod is Phase 5. Set
|
|
17
17
|
`links.dev` to the URL the Phase 1 `wrangler deploy` **actually printed** —
|
|
@@ -8,9 +8,10 @@ build the mirror yourself — the platform does it. This phase enables it.
|
|
|
8
8
|
Goal: signed-in users appear as rows in the app's `$users`, kept in sync with
|
|
9
9
|
Clerk (create/update/delete). Dev first, then prod.
|
|
10
10
|
|
|
11
|
-
Human obligation: create a Clerk webhook endpoint and
|
|
12
|
-
(`whsec_…`) into
|
|
13
|
-
|
|
11
|
+
Human obligation: create a Clerk webhook endpoint and get its signing secret
|
|
12
|
+
(`whsec_…`) into the tenant vault — piped through `secrets set` when a command
|
|
13
|
+
can print it, or pasted into Studio (both write-only). That plus the Phase-3
|
|
14
|
+
`clerk auth login` are the only human Clerk steps.
|
|
14
15
|
|
|
15
16
|
## How `$users` fills — two paths
|
|
16
17
|
|
|
@@ -37,12 +38,18 @@ Enable it here.
|
|
|
37
38
|
Clerk dashboard → Webhooks → Add Endpoint. (`whsec_` is a real secret — never
|
|
38
39
|
print/commit it; it is the only thing you paste, and it goes into the vault,
|
|
39
40
|
not chat.)
|
|
40
|
-
2. **Store the secret in the tenant vault** as `clerk_webhook_secret`, write-only
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
2. **Store the secret in the tenant vault** as `clerk_webhook_secret`, write-only.
|
|
42
|
+
If any command can print the `whsec_` (Clerk CLI, Svix API), pipe it without
|
|
43
|
+
it ever being displayed:
|
|
44
|
+
`<command that prints whsec_…> | npx @odla-ai/cli secrets set clerk_webhook_secret --env dev --stdin`.
|
|
45
|
+
Otherwise a human pastes it into Studio's secret UI (`odla.ai/studio` → the
|
|
46
|
+
app → the env → secrets) — same write-only slot. The server decrypts it only
|
|
47
|
+
to verify inbound events (svix HMAC over `id.timestamp.body`).
|
|
43
48
|
3. **(Optional) `sk_…`** — the webhook payload already carries email/name/image,
|
|
44
|
-
so the basic mirror needs only `whsec_`. Add the Clerk backend key
|
|
45
|
-
|
|
49
|
+
so the basic mirror needs only `whsec_`. Add the Clerk backend key only if
|
|
50
|
+
you later need to resolve data the webhook doesn't send (invites, member
|
|
51
|
+
lookups): `npx @odla-ai/cli secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY`
|
|
52
|
+
stores it in the reserved `$clerk_secret` slot, write-only.
|
|
46
53
|
|
|
47
54
|
## Verify
|
|
48
55
|
|
|
@@ -24,10 +24,10 @@ instance, never a second Clerk app per env; final go/no-go at each step.
|
|
|
24
24
|
`secrets push --env prod --yes` only to retry the transfer (see
|
|
25
25
|
references/secrets-map.md).
|
|
26
26
|
3. Build, then run the passive pre-cutover security gate:
|
|
27
|
-
first require `npm view @odla-ai/security@0.2.
|
|
27
|
+
first require `npm view @odla-ai/security@0.2.2 version` to succeed. An
|
|
28
28
|
exact-version `E404` means the release is unavailable and blocks cutover;
|
|
29
29
|
it is not a clean scan and does not prove the package name is absent. Run
|
|
30
|
-
`npm i -D --save-exact @odla-ai/security@0.2.
|
|
30
|
+
`npm i -D --save-exact @odla-ai/security@0.2.2` followed by
|
|
31
31
|
`npx odla-security scan . --profile odla --out .odla/security/pre-cutover --fail-on high --fail-on-candidates critical`.
|
|
32
32
|
Review `REPORT.md`; a candidate is a lead, not confirmation, and a baseline
|
|
33
33
|
requires a concrete reason, owner, and expiry.
|
|
@@ -35,6 +35,12 @@ instance, never a second Clerk app per env; final go/no-go at each step.
|
|
|
35
35
|
with `npx @odla-ai/cli security run . --env prod --ack-redacted-source`.
|
|
36
36
|
The platform owns provider credentials and attributes the bounded,
|
|
37
37
|
independent-model run to this app; never request provider keys locally.
|
|
38
|
+
For a repeatable commit-pinned review, the human can first approve
|
|
39
|
+
`security github connect --env prod`, then the agent runs `security plan`,
|
|
40
|
+
lists `security sources`, and runs `security run --source <id> --ref <ref>
|
|
41
|
+
--env prod --plan-digest <digest-from-security-plan>
|
|
42
|
+
--ack-redacted-source`. Never request a PAT; GitHub read approval is not
|
|
43
|
+
snippet-disclosure consent.
|
|
38
44
|
4. `npx wrangler deploy` (first prod deploy). Verify
|
|
39
45
|
`/api/health` and the parity paths on the prod workers.dev URL.
|
|
40
46
|
5. `npx @odla-ai/cli smoke --env prod`.
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
| Value | Lives in | Handling |
|
|
6
6
|
|---|---|---|
|
|
7
7
|
| Clerk publishable key (`pk_test_`/`pk_live_`) | registry, via provision → setAuth | public by design; the one value a human may paste into chat; fine inline in odla.config.mjs |
|
|
8
|
-
| Clerk webhook secret (`whsec_…`) | tenant vault (`clerk_webhook_secret`) | only for auth mode "full"; entered in Studio
|
|
9
|
-
| Clerk secret key (`sk_test_`/`sk_live_`) | not
|
|
8
|
+
| Clerk webhook secret (`whsec_…`) | tenant vault (`clerk_webhook_secret`) | only for auth mode "full"; piped with `secrets set clerk_webhook_secret --env <env> --stdin`, or entered in Studio; never wrangler, never chat |
|
|
9
|
+
| Clerk secret key (`sk_test_`/`sk_live_`) | not needed for this journey | if a later feature needs user resolution/invites, pipe it with `secrets set-clerk-key --env <env>` (reserved `$clerk_secret`, write-only); never chat |
|
|
10
10
|
| LLM provider key | tenant vault | env var in the HUMAN's shell for one provision run; never wrangler vars, never git, never chat |
|
|
11
11
|
| `odla_sk_…` (tenant db key) | wrangler secret `ODLA_API_KEY` + `.odla/credentials.local.json` (0600) + `.dev.vars` | present when db is enabled; move it only with the pipeline below |
|
|
12
12
|
| `odla_dev_…` (developer token) | `.odla/dev-token.json` (0600) | ~24h lifetime, provision-time only; never deployed |
|
|
@@ -42,6 +42,20 @@ Manual fallback (identical mechanics, if the CLI is unavailable):
|
|
|
42
42
|
(For prod, use `c.envs.prod.dbKey` and drop `--env` — the top-level
|
|
43
43
|
wrangler env is prod.)
|
|
44
44
|
|
|
45
|
+
## Moving a tenant-vault secret (never through the transcript)
|
|
46
|
+
|
|
47
|
+
`secrets set` fills the same write-only vault slot as Studio's Secrets panel,
|
|
48
|
+
reading the value only from stdin or a named env var — never argv — so a
|
|
49
|
+
producer command pipes straight into the vault:
|
|
50
|
+
|
|
51
|
+
<command that prints the value> | npx @odla-ai/cli secrets set clerk_webhook_secret --env dev --stdin
|
|
52
|
+
npx @odla-ai/cli secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY
|
|
53
|
+
|
|
54
|
+
The value cannot be read back by the CLI, Studio, or the developer token —
|
|
55
|
+
only by that tenant's app API key (reserved `$` secrets not even then). Studio
|
|
56
|
+
remains the fallback when no producer command exists (a human copies from the
|
|
57
|
+
Clerk dashboard between two browser tabs). Prod-named envs require `--yes`.
|
|
58
|
+
|
|
45
59
|
## Standing rules
|
|
46
60
|
|
|
47
61
|
- Never `cat`, `head`, `grep -v`, or otherwise display `.dev.vars`,
|