@odla-ai/cli 0.7.1 → 0.8.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,6 +1,6 @@
1
1
  # @odla-ai/cli — LLM context
2
2
 
3
- > Agent-operable CLI for odla provisioning, scoped System AI administration, hosted security runs, Worker secrets, and smoke checks.
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.7.1 version
35
- npm i -D --save-exact @odla-ai/cli@0.7.1
34
+ npm view @odla-ai/cli@0.8.0 version
35
+ npm i -D --save-exact @odla-ai/cli@0.8.0
36
36
  ```
37
37
 
38
38
  ## Prerequisites
@@ -61,7 +61,13 @@ 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 security run . --env dev --ack-redacted-source
64
+ npx @odla-ai/cli security run . --env dev --ack-redacted-source
65
+ npx @odla-ai/cli security github connect --env dev # infers owner/name from git origin
66
+ npx @odla-ai/cli security plan --env dev
67
+ npx @odla-ai/cli security sources --env dev
68
+ npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
69
+ npx @odla-ai/cli security status <job-id>
70
+ npx @odla-ai/cli security report <job-id>
65
71
  npx odla-ai admin ai show
66
72
  npx odla-ai admin ai models
67
73
  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 +135,7 @@ when provisioning has not written `.odla/credentials.local.json`.
129
135
 
130
136
  The bundled build/migration skills add a passive pre-ship gate with
131
137
  `@odla-ai/security`: after the exact-version availability check, run
132
- `npm i -D --save-exact @odla-ai/security@0.2.1`, then
138
+ `npm i -D --save-exact @odla-ai/security@0.2.2`, then
133
139
  `npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical`.
134
140
  The passive scan remains a separate binary: it makes no model calls and does
135
141
  not execute target code. After explicit redacted-source approval,
@@ -155,12 +161,79 @@ carries a bounded `Retry-After` only when the upstream supplied one. The CLI
155
161
  does not echo arbitrary response text or silently change the admin-selected
156
162
  model.
157
163
  Before installing the exact release, require
158
- `npm view @odla-ai/security@0.2.1 version` to succeed before installation. An
164
+ `npm view @odla-ai/security@0.2.2 version` to succeed before installation. An
159
165
  exact-version `E404` means the release is unavailable, not that the security
160
166
  preflight passed, and does not prove the package name is absent. odla's own
161
167
  engineering environment uses an equivalent internal gate; customer projects
162
168
  should use the published `odla-security` command above.
163
169
 
170
+ For a server-side, commit-pinned review, connect one repository through odla's
171
+ GitHub App. Contents and Metadata access are read-only; optional Checks write
172
+ access publishes only a best-effort summary. The CLI infers `owner/name` from a
173
+ safe GitHub HTTPS or SSH `origin`; pass `--repo owner/name` when it cannot. It
174
+ opens GitHub's installation/authorization page and polls that exact odla
175
+ attempt. Finish each browser stage within its displayed 15-minute window:
176
+
177
+ ```bash
178
+ npx @odla-ai/cli security github connect --env dev
179
+ npx @odla-ai/cli security plan --env dev
180
+ npx @odla-ai/cli security sources --env dev
181
+ npx @odla-ai/cli security run --source <source-id> --ref main --env dev --plan-digest <digest-from-security-plan> --ack-redacted-source
182
+ ```
183
+
184
+ The source job follows by default, renders the normalized report, and applies
185
+ the same confirmed/candidate/incomplete gates; `--no-follow` only enqueues.
186
+ `security status` and `security report` inspect prior jobs, while `security
187
+ github disconnect --source <source-id> --env dev --yes` revokes the saved
188
+ source without requiring a GitHub-side uninstall. A source id belongs to one
189
+ app/environment; reconnect separately for another environment. Disconnect
190
+ cancels nonterminal work, while terminal reports remain until normal expiry.
191
+ The App is not subscribed to Push; an unexpectedly delivered Push event is
192
+ ignored and never starts a job. Source jobs default to `cloudflare-app`;
193
+ local-path jobs default to `odla`.
194
+
195
+ Clients send only an opaque odla source id, ref, profile, and explicit redacted
196
+ disclosure acknowledgement—never a PAT, installation id, provider key, or
197
+ provider/model selection override. The CLI deliberately displays the read-only
198
+ route identities in the consent preflight. odla.ai verifies access, pins the ref to a commit SHA,
199
+ fetches the bounded snapshot server-side, and uses the admin-selected System AI
200
+ routes. `security plan` shows both provider/model routes, exact policy
201
+ revisions and bounds, prompt/redaction/report contracts, readiness, retention,
202
+ and the no-execution boundary before consent. Each job requires
203
+ `--plan-digest` and binds acknowledgement to that complete digest; any covered
204
+ change requires a refreshed plan and acknowledgement.
205
+
206
+ After validating that plan digest, the CLI fetches and prints the atomic
207
+ server-authored execution intent for the exact app/environment, opaque source,
208
+ ref (or default branch), profile, redacted disclosure, and plan digest. Its
209
+ `executionDigest` is persisted with the job and sent back as
210
+ `expectedExecutionDigest`; a changed selection fails with `intent_conflict`
211
+ instead of running under generic consent. Repository display text is not digest
212
+ authority.
213
+ An omitted `--ref` is canonicalized to the source's current default-branch name
214
+ during preview. A branch-default change before enqueue invalidates the intent
215
+ instead of changing what the Workflow will acquire.
216
+
217
+ GitHub read access is not disclosure consent. No raw archive, file body, or
218
+ explicit source excerpt is deliberately persisted in the hosted report or
219
+ Workflow checkpoints. The private report may keep bounded,
220
+ best-effort credential-pattern-redacted model-derived prose and repository-relative paths for up to
221
+ 90 days; treat it as sensitive because it may reveal source semantics.
222
+ Provider/GitHub terms are separate.
223
+
224
+ The archive envelope is 16 MiB compressed, 48 MiB decompressed, 20,000 entries,
225
+ 10,000 files, 2 MiB per file, and 16 MiB aggregate file bytes. Links, special
226
+ entries, unsafe redirects, duplicate paths, and escaping paths are rejected.
227
+ The combined acquire/analyze step has zero automatic retries; after a terminal
228
+ failure, correct the ref/access/archive shape or wait for service recovery and
229
+ explicitly start a new job.
230
+
231
+ Checks are optional, best-effort summaries, use fixed conclusion semantics, and
232
+ are not the configurable CLI gate. Target code is not executed. Without an
233
+ injected isolated executor, hosted inference cannot mechanically confirm a
234
+ finding; critical candidates and incomplete coverage remain the operative
235
+ gates, and a clean result is not proof of security.
236
+
164
237
  `admin ai show|models|set|credentials|credential set|usage|audit` manages platform-funded System
165
238
  AI (`o11y.triage`, `security.discovery`, `security.validation`). It uses a
166
239
  separate, mode-`0600` `.odla/admin-token.local.json` cache. Studio displays the
@@ -284,37 +357,32 @@ If schema is present and rules are omitted, `defaultRules: "deny"` generates
284
357
  deny-all rules for every schema entity. That is the safe default for Workers
285
358
  that mediate reads and writes with an app key.
286
359
 
287
- ## API reference (generated from dist/index.d.ts, v0.7.1)
360
+ ## API reference (generated from dist/index.d.ts, v0.8.0)
288
361
 
289
362
  ```ts
290
363
  import { SecurityReport, PlatformSecurityRun } from '@odla-ai/security';
291
364
 
365
+ /** Injectable side-effect boundaries used to run and test the command dispatcher. */
366
+ interface CliDependencies {
367
+ fetch?: typeof fetch;
368
+ openUrl?: (url: string) => Promise<void>;
369
+ pollWait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
370
+ readGitOrigin?: (cwd: string) => Promise<string>;
371
+ confirm?: (message: string) => Promise<boolean>;
372
+ stdout?: Pick<typeof console, "log" | "error">;
373
+ }
292
374
  /**
293
- * Parse CLI arguments and dispatch to the matching command — the entry point
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).
375
+ * Parse CLI arguments and dispatch the matching odla command.
298
376
  *
299
- * Recognized commands: `version`/`-v`/`--version` and `help`/`-h`/`--help` print
300
- * and return; `init` scaffolds a project (requires `--app-id` and `--name`);
301
- * `doctor` validates config offline; `capabilities` prints the responsibility
302
- * boundary; `admin ai` reads/updates purpose-specific platform AI policies,
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.
377
+ * The dispatcher covers project setup, platform AI administration, hosted and
378
+ * local security, provisioning, smoke checks, skill installation, and secret
379
+ * pushes. It rejects unknown commands, unsupported options, extra positionals,
380
+ * missing values, and errors returned by the selected command.
313
381
  *
314
- * @param argv Argument vector without the node/script prefix (default
315
- * `process.argv.slice(2)`).
382
+ * @param argv Argument vector without the node/script prefix.
383
+ * @param dependencies Optional side-effect overrides for embedding and tests.
316
384
  */
317
- declare function runCli(argv?: string[]): Promise<void>;
385
+ declare function runCli(argv?: string[], dependencies?: CliDependencies): Promise<void>;
318
386
 
319
387
  /** One exact platform scope obtainable through the admin device flow. */
320
388
  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 +451,10 @@ declare function adminAi(options: AdminAiOptions): Promise<void>;
383
451
  /** Stable, machine-readable division of work between the CLI, coding agent,
384
452
  * human operator, and Studio. Printed by `odla-ai capabilities --json`. */
385
453
  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"];
454
+ 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", "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
455
  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"];
456
+ 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"];
457
+ 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
458
  };
391
459
  type Output = Pick<typeof console, "log">;
392
460
  /** Print the stable responsibility boundary for humans or agent tooling. */
@@ -714,6 +782,338 @@ interface HostedSecurityResult {
714
782
  */
715
783
  declare function runHostedSecurity(options: RunHostedSecurityOptions): Promise<HostedSecurityResult>;
716
784
 
785
+ /** GitHub repository connection persisted by odla.ai. GitHub credentials and
786
+ * installation identifiers are deliberately absent from this client view. */
787
+ interface GitHubSecuritySource {
788
+ sourceId: string;
789
+ repository: string;
790
+ installationAccount: string;
791
+ defaultBranch: string;
792
+ env: string;
793
+ status: "active" | "disabled" | "deleted";
794
+ connectedAt: string;
795
+ }
796
+ /** One browser-mediated GitHub App installation attempt. */
797
+ interface GitHubConnectionAttempt {
798
+ attemptId: string;
799
+ installUrl: string;
800
+ expiresAt: string;
801
+ }
802
+ /** Poll-safe state for a GitHub App installation attempt. */
803
+ interface GitHubConnectionStatus {
804
+ attemptId: string;
805
+ status: "pending" | "connected" | "expired" | "failed";
806
+ connectionId?: string;
807
+ sourceId?: string;
808
+ repository?: string;
809
+ installationAccount?: string;
810
+ failureCode?: string;
811
+ }
812
+ /** Model identity selected by platform policy. No provider credential is
813
+ * returned to the CLI. */
814
+ interface HostedSecurityRoute {
815
+ provider: string;
816
+ model: string;
817
+ policyVersion?: number;
818
+ }
819
+ /** One purpose-specific System AI route disclosed before a hosted review. */
820
+ interface HostedSecurityPlanRoute extends HostedSecurityRoute {
821
+ purpose: "security.discovery" | "security.validation";
822
+ policyVersion: number;
823
+ enabled: boolean;
824
+ credentialReady: boolean;
825
+ maxCallsPerRun: number;
826
+ maxInputBytes: number;
827
+ maxOutputTokens: number;
828
+ }
829
+ /** Owner-readable, versioned disclosure preflight. Provider/model identities
830
+ * and bounds are non-secret; the full plan digest binds subsequent consent so
831
+ * an admin edit or processing-contract release cannot silently change it. */
832
+ interface HostedSecurityPlan {
833
+ env: string;
834
+ /** Versioned disclosure and processing contract covered by `planDigest`. */
835
+ consentContract: "odla.hosted-security-consent.v1";
836
+ /** Versioned private report projection covered by `planDigest`. */
837
+ reportProjection: "odla.hosted-security-report.v1";
838
+ /** Best-effort credential-pattern redaction contract covered by `planDigest`. */
839
+ redactionContract: "odla.best-effort-credential-pattern-redaction.v1";
840
+ /** Server-owned prompt bundle covered by `planDigest`. */
841
+ promptBundle: string;
842
+ /** Digest binding this exact disclosure plan to a later enqueue request. */
843
+ planDigest: `sha256:${string}`;
844
+ ready: boolean;
845
+ independent: boolean;
846
+ sourceDisclosure: "redacted";
847
+ targetExecution: false;
848
+ reportRetentionDays: number;
849
+ routes: {
850
+ discovery: HostedSecurityPlanRoute;
851
+ validation: HostedSecurityPlanRoute;
852
+ };
853
+ }
854
+ /** Server-authored preview of the exact source/ref/profile execution choice. */
855
+ interface HostedSecurityIntent {
856
+ executionContract: "odla.hosted-security-execution-consent.v1";
857
+ executionDigest: `sha256:${string}`;
858
+ appId: string;
859
+ env: string;
860
+ sourceId: string;
861
+ repository: string;
862
+ defaultBranch: string;
863
+ /** Explicit requested ref; omitted requests are canonicalized to the source's current default branch. */
864
+ requestedRef: string;
865
+ profile: "odla" | "cloudflare-app" | "generic";
866
+ sourceDisclosure: "redacted";
867
+ planDigest: `sha256:${string}`;
868
+ }
869
+ /** Atomic server response used to review both the processing plan and execution intent. */
870
+ interface HostedSecurityIntentResponse {
871
+ plan: HostedSecurityPlan;
872
+ intent: HostedSecurityIntent;
873
+ }
874
+ /** Lifecycle state reported for a hosted repository security job. */
875
+ type HostedSecurityJobStatus = "queued" | "acquiring" | "analyzing" | "completed" | "failed" | "cancelled";
876
+ /** Finding totals summarized on hosted job metadata. */
877
+ interface HostedSecurityCounts {
878
+ confirmed: number;
879
+ needsReproduction: number;
880
+ candidates: number;
881
+ }
882
+ /** Coverage-cell totals summarized on hosted job metadata. */
883
+ interface HostedSecurityCoverage {
884
+ completeCells?: number;
885
+ totalCells?: number;
886
+ shallowCells?: number;
887
+ blockedCells?: number;
888
+ unscheduledCells?: number;
889
+ budgetExhaustedCells?: number;
890
+ }
891
+ /** Bounded job metadata returned by odla.ai; it has no archive, file-body, or
892
+ * explicit source-excerpt field. */
893
+ interface HostedSecurityJob {
894
+ jobId: string;
895
+ env: string;
896
+ sourceId: string;
897
+ repository: string;
898
+ requestedRef?: string;
899
+ commitSha?: string;
900
+ status: HostedSecurityJobStatus;
901
+ runId?: string;
902
+ profile: string;
903
+ routes?: {
904
+ discovery: HostedSecurityRoute;
905
+ validation: HostedSecurityRoute;
906
+ };
907
+ sourceDisclosure: "redacted";
908
+ planDigest: `sha256:${string}`;
909
+ executionContract: "odla.hosted-security-execution-consent.v1";
910
+ executionDigest: `sha256:${string}`;
911
+ retentionDays: number;
912
+ snapshotDigest?: `sha256:${string}`;
913
+ fileCount?: number;
914
+ inputBytes?: number;
915
+ reportDigest?: `sha256:${string}`;
916
+ coverageStatus?: "not_run" | "complete" | "incomplete";
917
+ coverage?: HostedSecurityCoverage;
918
+ counts?: HostedSecurityCounts;
919
+ errorCode?: string;
920
+ createdAt: string;
921
+ updatedAt: string;
922
+ startedAt?: string;
923
+ completedAt?: string;
924
+ }
925
+ /** One normalized, bounded finding in a hosted security report. */
926
+ interface HostedSecurityReportFinding {
927
+ fingerprint: string;
928
+ kind: string;
929
+ attackClass: string;
930
+ title: string;
931
+ summary: string;
932
+ impact: string;
933
+ remediation?: string;
934
+ validationRationale?: string;
935
+ validationStatus?: string;
936
+ confidence: "low" | "medium" | "high";
937
+ severity: "critical" | "high" | "medium" | "low" | "informational";
938
+ disposition: string;
939
+ locations: Array<{
940
+ path: string;
941
+ line: number;
942
+ column?: number;
943
+ }>;
944
+ }
945
+ /** Bounded report projection with normalized model-derived prose and paths.
946
+ * Its schema has no dedicated raw-source, archive, source-excerpt, context
947
+ * packet, raw-provider-response, credential, execution-trace, or reproduction
948
+ * output field. Model prose may reveal or echo source semantics, so the report
949
+ * must remain private. */
950
+ interface HostedSecurityReport {
951
+ schemaVersion: 1;
952
+ jobId: string;
953
+ runId: string;
954
+ repository: string;
955
+ revision: string;
956
+ snapshotDigest: string;
957
+ reportDigest: string;
958
+ startedAt: string;
959
+ completedAt: string;
960
+ mode: "passive" | "autonomous";
961
+ coverageStatus: "not_run" | "complete" | "incomplete";
962
+ findingsReturned: number;
963
+ findingsTotal: number;
964
+ coverageReturned: number;
965
+ coverageTotal: number;
966
+ metrics: HostedSecurityCounts & {
967
+ rejected: number;
968
+ suppressed: number;
969
+ coverageCells: number;
970
+ shallowCells: number;
971
+ blockedCells: number;
972
+ unscheduledCells: number;
973
+ budgetExhaustedCells: number;
974
+ };
975
+ provenance: {
976
+ harnessVersion: string;
977
+ promptBundle: string;
978
+ profile: {
979
+ id: string;
980
+ version: string;
981
+ };
982
+ independentValidation: boolean;
983
+ discovery?: {
984
+ provider?: string;
985
+ model?: string;
986
+ };
987
+ validation?: {
988
+ provider?: string;
989
+ model?: string;
990
+ };
991
+ };
992
+ coverage: Array<{
993
+ area: string;
994
+ attackClass: string;
995
+ attempts: number;
996
+ candidateCount: number;
997
+ state: string;
998
+ }>;
999
+ limitations: string[];
1000
+ findings: HostedSecurityReportFinding[];
1001
+ }
1002
+ /** Common authenticated transport controls for hosted-security requests. */
1003
+ interface HostedSecurityRequestOptions {
1004
+ platform: string;
1005
+ token: string;
1006
+ fetch?: typeof fetch;
1007
+ signal?: AbortSignal;
1008
+ }
1009
+ /** Options for browser-mediated connection of one exact GitHub repository. */
1010
+ interface ConnectGitHubSecuritySourceOptions extends HostedSecurityRequestOptions {
1011
+ appId: string;
1012
+ env: string;
1013
+ /** Exact owner/name repository requested before GitHub approval. */
1014
+ repository: string;
1015
+ /** false prints but does not launch the install URL. Defaults to true. */
1016
+ open?: boolean;
1017
+ openInstallUrl?: (url: string) => Promise<void>;
1018
+ /** Poll interval. The server attempt expiry remains authoritative. */
1019
+ pollIntervalMs?: number;
1020
+ pollTimeoutMs?: number;
1021
+ wait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
1022
+ now?: () => number;
1023
+ stdout?: Pick<typeof console, "log">;
1024
+ }
1025
+ /** Options for listing GitHub sources or reading the plan for one app environment. */
1026
+ interface ListGitHubSecuritySourcesOptions extends HostedSecurityRequestOptions {
1027
+ appId: string;
1028
+ env: string;
1029
+ }
1030
+ /** Inputs for fetching the server-authored execution preview before enqueue. */
1031
+ interface GetHostedSecurityIntentOptions extends HostedSecurityRequestOptions {
1032
+ appId: string;
1033
+ env: string;
1034
+ sourceId: string;
1035
+ ref?: string;
1036
+ profile?: "odla" | "cloudflare-app" | "generic";
1037
+ }
1038
+ /** Options for revoking odla access to one saved GitHub source. */
1039
+ interface DisconnectGitHubSecuritySourceOptions extends HostedSecurityRequestOptions {
1040
+ appId: string;
1041
+ sourceId: string;
1042
+ }
1043
+ /** Options for enqueueing a commit-pinned hosted security review. */
1044
+ interface StartHostedSecurityJobOptions extends HostedSecurityRequestOptions {
1045
+ appId: string;
1046
+ env: string;
1047
+ sourceId: string;
1048
+ ref?: string;
1049
+ profile?: "odla" | "cloudflare-app" | "generic";
1050
+ clientRequestId?: string;
1051
+ /** Versions shown in the disclosure preflight. The server rejects stale consent. */
1052
+ expectedPolicyVersions: {
1053
+ discovery: number;
1054
+ validation: number;
1055
+ };
1056
+ /** Digest copied from the plan the app owner explicitly reviewed. */
1057
+ expectedPlanDigest: `sha256:${string}`;
1058
+ /** Digest copied from the exact server-authored source/ref/profile preview. */
1059
+ expectedExecutionDigest: `sha256:${string}`;
1060
+ /** Explicit disclosure acknowledgement; GitHub read access alone is not consent. */
1061
+ sourceDisclosureAck?: "redacted";
1062
+ }
1063
+ /** Options for reading one hosted security job or its report. */
1064
+ interface GetHostedSecurityJobOptions extends HostedSecurityRequestOptions {
1065
+ appId: string;
1066
+ jobId: string;
1067
+ }
1068
+ /** Options for listing hosted security jobs in one app environment. */
1069
+ interface ListHostedSecurityJobsOptions extends HostedSecurityRequestOptions {
1070
+ appId: string;
1071
+ env: string;
1072
+ }
1073
+ /** Polling controls for following one hosted job to a terminal state. */
1074
+ interface FollowHostedSecurityJobOptions extends GetHostedSecurityJobOptions {
1075
+ pollIntervalMs?: number;
1076
+ pollTimeoutMs?: number;
1077
+ wait?: (milliseconds: number, signal?: AbortSignal) => Promise<void>;
1078
+ now?: () => number;
1079
+ onUpdate?: (job: Readonly<HostedSecurityJob>) => void;
1080
+ }
1081
+
1082
+ /** Begin GitHub App installation, launch its server-issued GitHub URL, and
1083
+ * poll the exact attempt until the callback has connected a source. */
1084
+ declare function connectGitHubSecuritySource(options: ConnectGitHubSecuritySourceOptions): Promise<GitHubConnectionStatus>;
1085
+ /** List repository sources the current app owner connected for one environment. */
1086
+ declare function listGitHubSecuritySources(options: ListGitHubSecuritySourcesOptions): Promise<GitHubSecuritySource[]>;
1087
+ /** Revoke odla's use of one saved source without requiring a GitHub-side App
1088
+ * uninstall. The server marks the source deleted and rejects new jobs. */
1089
+ declare function disconnectGitHubSecuritySource(options: DisconnectGitHubSecuritySourceOptions): Promise<void>;
1090
+ /** Parse an HTTPS, ssh://, or git@github.com origin without retaining embedded
1091
+ * credentials. Only github.com owner/name remotes are accepted. */
1092
+ declare function repositoryFromGitRemote(remoteInput: string): string;
1093
+ /** Read the current repository's origin without invoking a shell. */
1094
+ declare function inferGitHubRepository(cwd?: string, readOrigin?: (cwd: string) => Promise<string>): Promise<string>;
1095
+
1096
+ /** Fetch the atomic processing-plan and source/ref/profile execution preview. */
1097
+ declare function getHostedSecurityIntent(options: GetHostedSecurityIntentOptions): Promise<HostedSecurityIntentResponse>;
1098
+
1099
+ /** Read the admin-selected routes, budgets, readiness, disclosure boundary,
1100
+ * and digest that must be acknowledged when a hosted review is enqueued. */
1101
+ declare function getHostedSecurityPlan(options: ListGitHubSecuritySourcesOptions): Promise<HostedSecurityPlan>;
1102
+ /** Enqueue a commit-pinned server-side review. The server resolves sourceId to
1103
+ * its saved GitHub App installation; clients cannot choose an installation or
1104
+ * pass a PAT. */
1105
+ declare function startHostedSecurityJob(options: StartHostedSecurityJobOptions): Promise<HostedSecurityJob>;
1106
+ /** Read one bounded hosted job status without an archive or explicit source excerpt. */
1107
+ declare function getHostedSecurityJob(options: GetHostedSecurityJobOptions): Promise<HostedSecurityJob>;
1108
+ /** List bounded, newest-first job metadata for an app environment. */
1109
+ declare function listHostedSecurityJobs(options: ListHostedSecurityJobsOptions): Promise<HostedSecurityJob[]>;
1110
+ /** Poll one exact job until it reaches a terminal state. */
1111
+ declare function followHostedSecurityJob(options: FollowHostedSecurityJobOptions): Promise<HostedSecurityJob>;
1112
+ /** Fetch the bounded normalized report projection for a completed hosted job. */
1113
+ declare function getHostedSecurityReport(options: GetHostedSecurityJobOptions): Promise<HostedSecurityReport>;
1114
+ /** Return whether a hosted job status can no longer transition. */
1115
+ declare function isTerminalHostedSecurityStatus(status: HostedSecurityJobStatus): boolean;
1116
+
717
1117
  /** Agent harnesses with a first-class local adapter. */
718
1118
  declare const AGENT_HARNESSES: readonly ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
719
1119
  /** A supported coding-agent harness. `agents` is the portable `AGENTS.md` adapter. */
@@ -793,5 +1193,5 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
793
1193
  */
794
1194
  declare function smoke(options: SmokeOptions): Promise<void>;
795
1195
 
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 };
1196
+ export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, CAPABILITIES, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type GitHubConnectionAttempt, type GitHubConnectionStatus, type GitHubSecuritySource, type HarnessInstallResult, type HostedSecurityCounts, type HostedSecurityCoverage, type HostedSecurityIntent, type HostedSecurityIntentResponse, type HostedSecurityJob, type HostedSecurityJobStatus, type HostedSecurityPlan, type HostedSecurityPlanRoute, type HostedSecurityProfile, type HostedSecurityReport, type HostedSecurityReportFinding, type HostedSecurityResult, type HostedSecurityRoute, 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 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, smoke, startHostedSecurityJob };
797
1197
  ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@odla-ai/cli",
3
- "version": "0.7.1",
4
- "description": "Agent-operable CLI for odla provisioning, scoped System AI administration, hosted security runs, Worker secrets, and smoke checks.",
3
+ "version": "0.8.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.1"
65
+ "@odla-ai/security": "^0.2.2"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/node": "^22.10.0",
@@ -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.1 version`. An exact-version `E404` means the
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.1
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.1 version` to succeed before installing it. An
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.1
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.7.1 version` to succeed, then run
14
- `npm i -D --save-exact @odla-ai/cli@0.7.1` and `npm i @odla-ai/db`.
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** —