@odla-ai/cli 0.8.0 → 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 +20 -2
- package/dist/bin.cjs +116 -32
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-BHAEDEL2.js → chunk-MJYQ7YDH.js} +112 -26
- package/dist/chunk-MJYQ7YDH.js.map +1 -0
- package/dist/index.cjs +120 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -219
- package/dist/index.d.ts +54 -219
- package/dist/index.js +5 -1
- package/llms.txt +75 -222
- package/package.json +1 -1
- package/skills/odla-migrate/references/phase-3b-user-sync.md +15 -8
- package/skills/odla-migrate/references/secrets-map.md +16 -2
- package/dist/chunk-BHAEDEL2.js.map +0 -1
package/llms.txt
CHANGED
|
@@ -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,6 +61,8 @@ 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 secrets set clerk_webhook_secret --env dev --stdin
|
|
65
|
+
npx odla-ai secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY
|
|
64
66
|
npx @odla-ai/cli security run . --env dev --ack-redacted-source
|
|
65
67
|
npx @odla-ai/cli security github connect --env dev # infers owner/name from git origin
|
|
66
68
|
npx @odla-ai/cli security plan --env dev
|
|
@@ -268,6 +270,22 @@ redacted plan without spawning anything. For the normal path, use
|
|
|
268
270
|
`provision --write-dev-vars --push-secrets` so service enablement, credential
|
|
269
271
|
issuance, local config, and deployed secret transfer stay one composition.
|
|
270
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
|
+
|
|
271
289
|
## Who does what
|
|
272
290
|
|
|
273
291
|
If a change is deterministic from `odla.config.mjs`, the CLI owns it: app and
|
|
@@ -357,10 +375,12 @@ If schema is present and rules are omitted, `defaultRules: "deny"` generates
|
|
|
357
375
|
deny-all rules for every schema entity. That is the safe default for Workers
|
|
358
376
|
that mediate reads and writes with an app key.
|
|
359
377
|
|
|
360
|
-
## API reference (generated from dist/index.d.ts, v0.
|
|
378
|
+
## API reference (generated from dist/index.d.ts, v0.9.0)
|
|
361
379
|
|
|
362
380
|
```ts
|
|
363
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';
|
|
364
384
|
|
|
365
385
|
/** Injectable side-effect boundaries used to run and test the command dispatcher. */
|
|
366
386
|
interface CliDependencies {
|
|
@@ -451,7 +471,7 @@ declare function adminAi(options: AdminAiOptions): Promise<void>;
|
|
|
451
471
|
/** Stable, machine-readable division of work between the CLI, coding agent,
|
|
452
472
|
* human operator, and Studio. Printed by `odla-ai capabilities --json`. */
|
|
453
473
|
declare const CAPABILITIES: {
|
|
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"];
|
|
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"];
|
|
455
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"];
|
|
456
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"];
|
|
457
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"];
|
|
@@ -727,6 +747,47 @@ interface SecretsPushOptions {
|
|
|
727
747
|
*/
|
|
728
748
|
declare function secretsPush(options: SecretsPushOptions): Promise<void>;
|
|
729
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
|
+
|
|
730
791
|
/** Built-in security coverage profile selected for a hosted reasoning run. */
|
|
731
792
|
type HostedSecurityProfile = "odla" | "cloudflare-app" | "generic";
|
|
732
793
|
/** Exact attribution and authorization need supplied to a lazy token callback. */
|
|
@@ -782,223 +843,15 @@ interface HostedSecurityResult {
|
|
|
782
843
|
*/
|
|
783
844
|
declare function runHostedSecurity(options: RunHostedSecurityOptions): Promise<HostedSecurityResult>;
|
|
784
845
|
|
|
785
|
-
/**
|
|
786
|
-
*
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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
|
-
}
|
|
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
|
+
|
|
1002
855
|
/** Common authenticated transport controls for hosted-security requests. */
|
|
1003
856
|
interface HostedSecurityRequestOptions {
|
|
1004
857
|
platform: string;
|
|
@@ -1193,5 +1046,5 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
|
|
|
1193
1046
|
*/
|
|
1194
1047
|
declare function smoke(options: SmokeOptions): Promise<void>;
|
|
1195
1048
|
|
|
1196
|
-
export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, CAPABILITIES, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type
|
|
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 };
|
|
1197
1050
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odla-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
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",
|
|
@@ -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
|
|
|
@@ -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`,
|