@odla-ai/cli 0.11.2 → 0.13.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/dist/index.d.cts CHANGED
@@ -2,6 +2,10 @@ import { SecurityReport, PlatformSecurityRun } from '@odla-ai/security';
2
2
  import { HostedSecurityJob, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityIntentResponse, HostedSecurityPlan, HostedSecurityReport, HostedSecurityJobStatus } from '@odla-ai/security/hosted';
3
3
  export { GitHubConnectionAttempt, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityCounts, HostedSecurityCoverage, HostedSecurityIntent, HostedSecurityIntentResponse, HostedSecurityJob, HostedSecurityJobStatus, HostedSecurityPlan, HostedSecurityPlanRoute, HostedSecurityReport, HostedSecurityReportFinding, HostedSecurityRoute } from '@odla-ai/security/hosted';
4
4
 
5
+ /** Map a CLI failure to its process exit code: 75 (EX_TEMPFAIL) marks a
6
+ * handshake that is still awaiting human approval — the pending state is
7
+ * persisted and rerunning the same command resumes it — everything else is 1. */
8
+ declare function exitCodeFor(err: unknown): number;
5
9
  /** Injectable side-effect boundaries used to run and test the command dispatcher. */
6
10
  interface CliDependencies {
7
11
  fetch?: typeof fetch;
@@ -95,8 +99,8 @@ declare function adminAi(options: AdminAiOptions): Promise<void>;
95
99
  /** Stable, machine-readable division of work between the CLI, coding agent,
96
100
  * human operator, and Studio. Printed by `odla-ai capabilities --json`. */
97
101
  declare const CAPABILITIES: {
98
- readonly cli: readonly ["start an email-bound device request without accepting a password or user session token", "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", "apply read-only Google calendar mirror and public booking-page config, then drive state-bound consent, status, discovery, resync, and disconnect flows", "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"];
99
- 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", "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"];
102
+ readonly cli: readonly ["start an email-bound device request without accepting a password or user session token", "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", "compose declared app-capability schema/rules, create guarded seeds when absent, and configure platform AI, auth, and deployment links", "apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)", "validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes", "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"];
103
+ readonly agent: readonly ["install and import the selected odla SDKs", "wrap the Worker with withObservability and choose useful telemetry", "install capability packages, mount their runtime routes, and make application-specific schema, rules, auth, UI, and migration decisions", "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"];
100
104
  readonly human: readonly ["provide the existing odla account email, then sign in and explicitly review/approve the exact device code", "review mirrored calendar/attendee disclosure and complete the server-issued Google consent flow", "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"];
101
105
  readonly studio: readonly ["view telemetry and environment state", "let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them", "review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens", "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"];
102
106
  };
@@ -104,29 +108,28 @@ type Output = Pick<typeof console, "log">;
104
108
  /** Print the stable responsibility boundary for humans or agent tooling. */
105
109
  declare function printCapabilities(json?: boolean, out?: Output): void;
106
110
 
107
- declare const CALENDAR_STATES: readonly ["not_connected", "authorizing", "needs_sync", "initial_sync", "healthy", "degraded", "disconnected", "failed"];
108
- /** Connection lifecycle reported by the registry; mirrors the platform's calendar states. */
111
+ declare const CALENDAR_STATES: readonly ["not_connected", "authorizing", "healthy", "degraded", "disconnected", "failed"];
112
+ /** Connection lifecycle reported by the registry; mirrors the platform's
113
+ * calendar states. `degraded` means connected but re-consent is required. */
109
114
  type CalendarConnectionState = (typeof CALENDAR_STATES)[number];
110
- /** Owner-visible, secret-free calendar connection and sync health. */
115
+ /** Owner-visible, secret-free calendar connection health. */
111
116
  interface CalendarStatus {
112
117
  env: string;
113
118
  enabled: boolean;
114
119
  /** True when the platform has provider credentials for this connection. */
115
120
  connected: boolean;
121
+ /** True when the grant covers booking writes; false means reconnect. */
122
+ writable: boolean;
116
123
  provider: "google" | null;
117
124
  status: CalendarConnectionState;
118
- access?: "read";
125
+ access?: "book";
126
+ /** Calendar that receives created bookings. */
127
+ bookingCalendarId?: string;
128
+ /** Calendars consulted for FreeBusy availability. */
119
129
  calendars: string[];
120
- attendeePolicy?: "full" | "hashed";
121
- /** True once a booking projection may have established the DB privacy policy. */
122
- attendeePolicyLocked?: boolean;
123
130
  bookingPageUrl?: string | null;
124
131
  grantedScopes: string[];
125
132
  attemptId?: string;
126
- lastSuccessfulSyncAt?: number;
127
- lastFullSyncAt?: number;
128
- watchExpiresAt?: number;
129
- bookingCount?: number;
130
133
  error?: {
131
134
  code?: string;
132
135
  message?: string;
@@ -142,9 +145,9 @@ interface AvailableGoogleCalendar {
142
145
  }
143
146
 
144
147
  /** Shared inputs for the calendar lifecycle commands (status, connect,
145
- * calendars, resync, disconnect): the project config path and env, plus
146
- * injectable token, output, browser, clock, and polling hooks so agents and
147
- * tests can drive the flow deterministically. */
148
+ * calendars, disconnect): the project config path and env, plus injectable
149
+ * token, output, browser, clock, and polling hooks so agents and tests can
150
+ * drive the flow deterministically. */
148
151
  interface CalendarLifecycleOptions {
149
152
  /** Path to the checked-in odla project config. */ configPath: string;
150
153
  /** Configured environment; defaults to dev when present. */ env?: string;
@@ -161,16 +164,17 @@ interface CalendarLifecycleOptions {
161
164
  now?: () => number;
162
165
  signal?: AbortSignal;
163
166
  }
164
- /** Read the owner-visible Google connection and mirror health for one env. */
167
+ /** Read the owner-visible Google connection and booking health for one env. */
165
168
  declare function calendarStatus(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
166
169
  /** List calendars visible to the connected Google identity for config authoring. */
167
170
  declare function calendarCalendars(options: CalendarLifecycleOptions): Promise<AvailableGoogleCalendar[]>;
168
- /** Apply the public booking page setting, then complete Google consent + initial sync. */
171
+ /** Apply the optional booking-page setting, then complete Google consent.
172
+ * A connection whose grant predates booking scopes (`degraded`) re-runs
173
+ * consent so the platform can create events and read FreeBusy. */
169
174
  declare function calendarConnect(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
170
- /** Ask the hosted connector for an immediate incremental/full recovery sync. */
171
- declare function calendarResync(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
172
- /** Stop watches and delete this connection's encrypted platform token/cursors
173
- * without purging the mirror or revoking the shared Google project grant. */
175
+ /** Delete this connection's encrypted platform tokens. No calendar data is
176
+ * stored anywhere, so there is nothing else to clean up; the shared Google
177
+ * project grant is not revoked. */
174
178
  declare function calendarDisconnect(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
175
179
 
176
180
  interface RunResult {
@@ -191,47 +195,82 @@ type AppRules = Record<string, {
191
195
  update?: string;
192
196
  delete?: string;
193
197
  }>;
198
+ /** One natural-key row the CLI creates only when an integration has not
199
+ * already been configured. Existing owner-edited rows are never overwritten. */
200
+ interface IntegrationSeed {
201
+ /** Stable label used in dry-run output and the exactly-once mutation id. */
202
+ id: string;
203
+ /** Namespace containing the singleton/seed row. */
204
+ ns: string;
205
+ /** Declared-unique natural key used to detect an existing row and prevent
206
+ * a concurrent create from overwriting it. */
207
+ key: {
208
+ attr: string;
209
+ value: string;
210
+ };
211
+ /** Complete initial attributes; the key attribute may be omitted. */
212
+ attrs: Record<string, unknown>;
213
+ }
214
+ /** One unauthenticated deployed-app check performed by `odla-ai smoke`. */
215
+ interface IntegrationProbe {
216
+ /** Absolute path beneath the configured environment link. */
217
+ path: string;
218
+ /** Expected unauthenticated HTTP status, normally 401. */
219
+ expectedStatus: number;
220
+ }
221
+ /** Structural, data-only capability descriptor consumed from
222
+ * `odla.config.mjs`. Capability packages do not depend on the CLI; their
223
+ * descriptors only need to conform to this shape. */
224
+ interface OdlaIntegration {
225
+ /** Stable lowercase capability id, unique within one project. */
226
+ id: string;
227
+ /** Human-readable capability name for plans and diagnostics. */
228
+ title: string;
229
+ /** Package that supplies the capability. */
230
+ npm: string;
231
+ /** Serialized odla-db schema fragment merged by namespace. */
232
+ schema?: {
233
+ entities: Record<string, unknown>;
234
+ links?: Record<string, unknown>;
235
+ };
236
+ /** Rule fragment merged by namespace. */
237
+ rules?: AppRules;
238
+ /** Rows created after schema/rules provisioning only when absent. */
239
+ seeds?: IntegrationSeed[];
240
+ /** Read-only unauthenticated checks run against `links.<env>`. */
241
+ probes?: IntegrationProbe[];
242
+ }
194
243
  interface ClerkAuthConfig {
195
244
  publishableKey?: string;
196
245
  audience?: string;
197
246
  mode?: "client" | "full";
198
247
  }
199
- /** Read-only event selection applied by the hosted Google calendar connector. */
200
- interface GoogleCalendarMatchConfig {
201
- /** Mirror only events whose summary begins with this value. */
202
- summaryPrefix?: string;
203
- /** Require the connected identity to organize the event. Defaults to true. */
204
- organizerSelf?: boolean;
205
- /** Require at least one attendee. Defaults to true. */
206
- requireAttendees?: boolean;
207
- }
208
- /** Google Calendar intent. OAuth credentials never belong in this config. */
248
+ /** Google Calendar booking intent. OAuth credentials never belong in this
249
+ * config, and no calendar data is mirrored — Google is the source of truth. */
209
250
  interface GoogleCalendarConfig {
210
- /** One through ten provider calendar ids to mirror in each odla environment. */
211
- calendars: Record<OdlaEnvName, string[]>;
212
- /** Public Google Appointment Schedule URL for static embed/link mode. */
251
+ /** One through ten calendars consulted for availability in each env. */
252
+ availabilityCalendars?: Record<OdlaEnvName, string[]>;
253
+ /** Retired read-mirror spelling of `availabilityCalendars` (still accepted). */
254
+ calendars?: Record<OdlaEnvName, string[]>;
255
+ /** Calendar that receives created bookings per env ("primary" or a concrete
256
+ * id). Defaults to the first availability calendar. */
257
+ bookingCalendar?: Record<OdlaEnvName, string>;
258
+ /** Optional public fallback link (retiring; first-party booking replaces it). */
213
259
  bookingPageUrl?: Record<OdlaEnvName, string | null>;
214
- match?: GoogleCalendarMatchConfig;
215
- /** Store normalized attendee addresses, or only SHA-256 match keys. */
216
- attendeePolicy?: "full" | "hashed";
217
260
  }
218
- /** The `calendar` block of `odla.config.mjs` — the Google read-mirror
219
- * settings; valid only alongside the db service. */
261
+ /** The `calendar` block of `odla.config.mjs` — the Google booking settings;
262
+ * valid only alongside the db service (booking rides the app's db key). */
220
263
  interface CalendarConfig {
221
264
  google: GoogleCalendarConfig;
222
265
  }
223
266
  /** Canonical, environment-specific config sent to the calendar controller. */
224
267
  interface CalendarServiceConfig extends Record<string, unknown> {
225
268
  provider: "google";
226
- access: "read";
227
- /** Deduplicated provider ids; the hosted connector accepts at most ten. */
228
- calendars: string[];
229
- match: {
230
- organizerSelf: boolean;
231
- requireAttendees: boolean;
232
- summaryPrefix?: string;
233
- };
234
- attendeePolicy: "full" | "hashed";
269
+ access: "book";
270
+ /** Calendar that receives created bookings. */
271
+ bookingCalendarId: string;
272
+ /** Deduplicated provider ids; the booking proxy accepts at most ten. */
273
+ availabilityCalendars: string[];
235
274
  }
236
275
  /** Authored `odla.config.mjs` contract consumed by doctor and provision. */
237
276
  interface OdlaProjectConfig {
@@ -243,6 +282,9 @@ interface OdlaProjectConfig {
243
282
  };
244
283
  envs?: OdlaEnvName[];
245
284
  services?: string[];
285
+ /** App capabilities composed into schema/rules/seeds. Integrations are npm
286
+ * modules, not hosted platform services, so they do not belong in services. */
287
+ integrations?: OdlaIntegration[];
246
288
  db?: {
247
289
  /** Inline SerializedSchema, JSON file, or JS module path exporting default/schema. */
248
290
  schema?: unknown;
@@ -325,6 +367,11 @@ interface ProvisionOptions {
325
367
  /** true forces browser launch, false disables it, undefined auto-opens whenever
326
368
  * this machine can show a browser (skipped in CI, over SSH, or with no display). */
327
369
  open?: boolean;
370
+ /** Cap the handshake-approval wait, in seconds. When it elapses the command
371
+ * exits with the pending handshake persisted; rerunning resumes it. Defaults
372
+ * to the handshake's own expiry on an interactive terminal and 90 seconds
373
+ * everywhere else (agent tool shells kill long-running commands). */
374
+ wait?: number;
328
375
  /** Test/embedding override for opening the approval URL. Defaults to the OS browser opener. */
329
376
  openApprovalUrl?: (url: string) => Promise<void>;
330
377
  /** Test/embedding override for calendar connection polling. */
@@ -346,6 +393,8 @@ interface ProvisionPlan {
346
393
  dbEndpoint: string;
347
394
  envs: string[];
348
395
  services: string[];
396
+ /** Configured app-capability ids in provisioning order. */
397
+ integrations: string[];
349
398
  hasSchema: boolean;
350
399
  hasRules: boolean;
351
400
  aiProvider?: string;
@@ -364,8 +413,8 @@ interface SmokeOptions {
364
413
  stdout?: Pick<typeof console, "log" | "error">;
365
414
  }
366
415
 
367
- /** The only Google OAuth scope the platform ever requests: read-only calendar events. */
368
- declare const GOOGLE_CALENDAR_READ_SCOPE = "https://www.googleapis.com/auth/calendar.events.readonly";
416
+ /** The Google OAuth scope that authorizes booking writes (events read/write). */
417
+ declare const GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
369
418
  /** Resolve the checked-in calendar block into one controller-safe env payload. */
370
419
  declare function calendarServiceConfig(cfg: LoadedProjectConfig, env: string): CalendarServiceConfig;
371
420
  /** Public Appointment Schedule URL to apply separately from connector config. */
@@ -378,13 +427,14 @@ interface DoctorOptions {
378
427
  /**
379
428
  * Validate and summarize an odla project config entirely offline — no network
380
429
  * calls and no file writes. Loads `configPath`, builds the provision plan, and
381
- * resolves the schema and rules (synthesizing deny-all rules from the schema
382
- * when `db.rules` is omitted and `db.defaultRules` isn't `false`), then prints a
383
- * summary line for the app, envs, services, schema entity count, rules namespace
384
- * count, and AI provider.
430
+ * resolves and collision-checks app plus integration schema/rules (synthesizing
431
+ * deny-all rules when `db.rules` is omitted and `db.defaultRules` isn't
432
+ * `false`), then prints the app, envs, services, integrations, schema entity
433
+ * count, rules namespace count, and AI provider.
385
434
  *
386
435
  * It then collects warnings for the mistakes provision can't fix silently: a
387
- * schema with no entities, a rules namespace with no matching entity, the `ai`
436
+ * schema with no entities, a rules namespace with no matching entity, an
437
+ * integration with incomplete rules or a seed targeting an unknown namespace, the `ai`
388
438
  * service enabled without an `ai.provider`, `auth.clerk` entries that reference
389
439
  * an unset env var (`$FOO`), an `ai.keyEnv` that isn't set (provider key upload
390
440
  * will be skipped), and — when `o11y` is enabled — any env whose local
@@ -442,8 +492,9 @@ declare function initProject(options: InitOptions): void;
442
492
  * developer token, creates the registry app when absent, enables each service
443
493
  * per env (AI also gets `setAi` with the configured provider/model), configures
444
494
  * Clerk auth and links, then per env mints or reuses credentials only for the
445
- * configured services, pushes the schema with the db key and the
446
- * rules with the developer token, and stores the provider key in the tenant
495
+ * configured services, collision-checks and merges integration schema/rules,
496
+ * pushes the composed schema with the db key and rules with the developer
497
+ * token, creates natural-key integration seeds only when absent, and stores the provider key in the tenant
447
498
  * vault when `ai.keyEnv` is set and present. Writes the credentials file
448
499
  * (mode 0600, gitignored) and, when requested, `.dev.vars`. Google Calendar
449
500
  * consent runs last, per env; when the platform connector itself is not yet
@@ -790,9 +841,10 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
790
841
  *
791
842
  * It then makes real network calls: fetches the registry public-config (and, when
792
843
  * `ai.provider` is configured, asserts the live provider matches), fetches the
793
- * live db schema with the db key and asserts every entity from the local schema
794
- * is present, and runs a `count` aggregate against the first entity to confirm
795
- * the db answers queries. Progress lines are printed and it ends with `ok`.
844
+ * live db schema with the db key and asserts every entity from the composed app
845
+ * plus integration schema is present, runs a `count` aggregate against the
846
+ * first entity, and checks each integration's deployed route probe anonymously
847
+ * against `links.<env>`. Progress lines are printed and it ends with `ok`.
796
848
  * Read-only — it writes nothing and throws on the first mismatch or failed
797
849
  * request.
798
850
  *
@@ -803,4 +855,4 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
803
855
  */
804
856
  declare function smoke(options: SmokeOptions): Promise<void>;
805
857
 
806
- export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, type AvailableGoogleCalendar, CAPABILITIES, type CalendarConfig, type CalendarConnectionState, type CalendarLifecycleOptions, type CalendarServiceConfig, type CalendarStatus, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, GOOGLE_CALENDAR_READ_SCOPE, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type GoogleCalendarConfig, type GoogleCalendarMatchConfig, 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, calendarBookingPageUrl, calendarCalendars, calendarConnect, calendarDisconnect, calendarResync, calendarServiceConfig, calendarStatus, 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 };
858
+ export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, type AvailableGoogleCalendar, CAPABILITIES, type CalendarConfig, type CalendarConnectionState, type CalendarLifecycleOptions, type CalendarServiceConfig, type CalendarStatus, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, GOOGLE_CALENDAR_EVENTS_SCOPE, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type GoogleCalendarConfig, type HarnessInstallResult, type HostedSecurityProfile, type HostedSecurityResult, type HostedSecurityTokenRequest, type IntegrationProbe, type IntegrationSeed, type ListGitHubSecuritySourcesOptions, type ListHostedSecurityJobsOptions, type LoadedProjectConfig, type LocalCredentials, type OdlaIntegration, 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, calendarBookingPageUrl, calendarCalendars, calendarConnect, calendarDisconnect, calendarServiceConfig, calendarStatus, connectGitHubSecuritySource, disconnectGitHubSecuritySource, doctor, exitCodeFor, followHostedSecurityJob, getHostedSecurityIntent, getHostedSecurityJob, getHostedSecurityPlan, getHostedSecurityReport, getScopedPlatformToken, inferGitHubRepository, initProject, installSkill, isTerminalHostedSecurityStatus, listGitHubSecuritySources, listHostedSecurityJobs, printCapabilities, provision, redactSecrets, repositoryFromGitRemote, runCli, runHostedSecurity, secretsPush, secretsSet, secretsSetClerkKey, smoke, startHostedSecurityJob };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,10 @@ import { SecurityReport, PlatformSecurityRun } from '@odla-ai/security';
2
2
  import { HostedSecurityJob, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityIntentResponse, HostedSecurityPlan, HostedSecurityReport, HostedSecurityJobStatus } from '@odla-ai/security/hosted';
3
3
  export { GitHubConnectionAttempt, GitHubConnectionStatus, GitHubSecuritySource, HostedSecurityCounts, HostedSecurityCoverage, HostedSecurityIntent, HostedSecurityIntentResponse, HostedSecurityJob, HostedSecurityJobStatus, HostedSecurityPlan, HostedSecurityPlanRoute, HostedSecurityReport, HostedSecurityReportFinding, HostedSecurityRoute } from '@odla-ai/security/hosted';
4
4
 
5
+ /** Map a CLI failure to its process exit code: 75 (EX_TEMPFAIL) marks a
6
+ * handshake that is still awaiting human approval — the pending state is
7
+ * persisted and rerunning the same command resumes it — everything else is 1. */
8
+ declare function exitCodeFor(err: unknown): number;
5
9
  /** Injectable side-effect boundaries used to run and test the command dispatcher. */
6
10
  interface CliDependencies {
7
11
  fetch?: typeof fetch;
@@ -95,8 +99,8 @@ declare function adminAi(options: AdminAiOptions): Promise<void>;
95
99
  /** Stable, machine-readable division of work between the CLI, coding agent,
96
100
  * human operator, and Studio. Printed by `odla-ai capabilities --json`. */
97
101
  declare const CAPABILITIES: {
98
- readonly cli: readonly ["start an email-bound device request without accepting a password or user session token", "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", "apply read-only Google calendar mirror and public booking-page config, then drive state-bound consent, status, discovery, resync, and disconnect flows", "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"];
99
- 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", "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"];
102
+ readonly cli: readonly ["start an email-bound device request without accepting a password or user session token", "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", "compose declared app-capability schema/rules, create guarded seeds when absent, and configure platform AI, auth, and deployment links", "apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)", "validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes", "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"];
103
+ readonly agent: readonly ["install and import the selected odla SDKs", "wrap the Worker with withObservability and choose useful telemetry", "install capability packages, mount their runtime routes, and make application-specific schema, rules, auth, UI, and migration decisions", "wire @odla-ai/calendar into trusted Worker code and keep the app admin key out of browsers"];
100
104
  readonly human: readonly ["provide the existing odla account email, then sign in and explicitly review/approve the exact device code", "review mirrored calendar/attendee disclosure and complete the server-issued Google consent flow", "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"];
101
105
  readonly studio: readonly ["view telemetry and environment state", "let signed-in users inventory/revoke their own agent grants and admins audit/global-revoke them", "review calendar connection, granted read scope, selected calendars, and sync health without exposing provider tokens", "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"];
102
106
  };
@@ -104,29 +108,28 @@ type Output = Pick<typeof console, "log">;
104
108
  /** Print the stable responsibility boundary for humans or agent tooling. */
105
109
  declare function printCapabilities(json?: boolean, out?: Output): void;
106
110
 
107
- declare const CALENDAR_STATES: readonly ["not_connected", "authorizing", "needs_sync", "initial_sync", "healthy", "degraded", "disconnected", "failed"];
108
- /** Connection lifecycle reported by the registry; mirrors the platform's calendar states. */
111
+ declare const CALENDAR_STATES: readonly ["not_connected", "authorizing", "healthy", "degraded", "disconnected", "failed"];
112
+ /** Connection lifecycle reported by the registry; mirrors the platform's
113
+ * calendar states. `degraded` means connected but re-consent is required. */
109
114
  type CalendarConnectionState = (typeof CALENDAR_STATES)[number];
110
- /** Owner-visible, secret-free calendar connection and sync health. */
115
+ /** Owner-visible, secret-free calendar connection health. */
111
116
  interface CalendarStatus {
112
117
  env: string;
113
118
  enabled: boolean;
114
119
  /** True when the platform has provider credentials for this connection. */
115
120
  connected: boolean;
121
+ /** True when the grant covers booking writes; false means reconnect. */
122
+ writable: boolean;
116
123
  provider: "google" | null;
117
124
  status: CalendarConnectionState;
118
- access?: "read";
125
+ access?: "book";
126
+ /** Calendar that receives created bookings. */
127
+ bookingCalendarId?: string;
128
+ /** Calendars consulted for FreeBusy availability. */
119
129
  calendars: string[];
120
- attendeePolicy?: "full" | "hashed";
121
- /** True once a booking projection may have established the DB privacy policy. */
122
- attendeePolicyLocked?: boolean;
123
130
  bookingPageUrl?: string | null;
124
131
  grantedScopes: string[];
125
132
  attemptId?: string;
126
- lastSuccessfulSyncAt?: number;
127
- lastFullSyncAt?: number;
128
- watchExpiresAt?: number;
129
- bookingCount?: number;
130
133
  error?: {
131
134
  code?: string;
132
135
  message?: string;
@@ -142,9 +145,9 @@ interface AvailableGoogleCalendar {
142
145
  }
143
146
 
144
147
  /** Shared inputs for the calendar lifecycle commands (status, connect,
145
- * calendars, resync, disconnect): the project config path and env, plus
146
- * injectable token, output, browser, clock, and polling hooks so agents and
147
- * tests can drive the flow deterministically. */
148
+ * calendars, disconnect): the project config path and env, plus injectable
149
+ * token, output, browser, clock, and polling hooks so agents and tests can
150
+ * drive the flow deterministically. */
148
151
  interface CalendarLifecycleOptions {
149
152
  /** Path to the checked-in odla project config. */ configPath: string;
150
153
  /** Configured environment; defaults to dev when present. */ env?: string;
@@ -161,16 +164,17 @@ interface CalendarLifecycleOptions {
161
164
  now?: () => number;
162
165
  signal?: AbortSignal;
163
166
  }
164
- /** Read the owner-visible Google connection and mirror health for one env. */
167
+ /** Read the owner-visible Google connection and booking health for one env. */
165
168
  declare function calendarStatus(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
166
169
  /** List calendars visible to the connected Google identity for config authoring. */
167
170
  declare function calendarCalendars(options: CalendarLifecycleOptions): Promise<AvailableGoogleCalendar[]>;
168
- /** Apply the public booking page setting, then complete Google consent + initial sync. */
171
+ /** Apply the optional booking-page setting, then complete Google consent.
172
+ * A connection whose grant predates booking scopes (`degraded`) re-runs
173
+ * consent so the platform can create events and read FreeBusy. */
169
174
  declare function calendarConnect(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
170
- /** Ask the hosted connector for an immediate incremental/full recovery sync. */
171
- declare function calendarResync(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
172
- /** Stop watches and delete this connection's encrypted platform token/cursors
173
- * without purging the mirror or revoking the shared Google project grant. */
175
+ /** Delete this connection's encrypted platform tokens. No calendar data is
176
+ * stored anywhere, so there is nothing else to clean up; the shared Google
177
+ * project grant is not revoked. */
174
178
  declare function calendarDisconnect(options: CalendarLifecycleOptions): Promise<CalendarStatus>;
175
179
 
176
180
  interface RunResult {
@@ -191,47 +195,82 @@ type AppRules = Record<string, {
191
195
  update?: string;
192
196
  delete?: string;
193
197
  }>;
198
+ /** One natural-key row the CLI creates only when an integration has not
199
+ * already been configured. Existing owner-edited rows are never overwritten. */
200
+ interface IntegrationSeed {
201
+ /** Stable label used in dry-run output and the exactly-once mutation id. */
202
+ id: string;
203
+ /** Namespace containing the singleton/seed row. */
204
+ ns: string;
205
+ /** Declared-unique natural key used to detect an existing row and prevent
206
+ * a concurrent create from overwriting it. */
207
+ key: {
208
+ attr: string;
209
+ value: string;
210
+ };
211
+ /** Complete initial attributes; the key attribute may be omitted. */
212
+ attrs: Record<string, unknown>;
213
+ }
214
+ /** One unauthenticated deployed-app check performed by `odla-ai smoke`. */
215
+ interface IntegrationProbe {
216
+ /** Absolute path beneath the configured environment link. */
217
+ path: string;
218
+ /** Expected unauthenticated HTTP status, normally 401. */
219
+ expectedStatus: number;
220
+ }
221
+ /** Structural, data-only capability descriptor consumed from
222
+ * `odla.config.mjs`. Capability packages do not depend on the CLI; their
223
+ * descriptors only need to conform to this shape. */
224
+ interface OdlaIntegration {
225
+ /** Stable lowercase capability id, unique within one project. */
226
+ id: string;
227
+ /** Human-readable capability name for plans and diagnostics. */
228
+ title: string;
229
+ /** Package that supplies the capability. */
230
+ npm: string;
231
+ /** Serialized odla-db schema fragment merged by namespace. */
232
+ schema?: {
233
+ entities: Record<string, unknown>;
234
+ links?: Record<string, unknown>;
235
+ };
236
+ /** Rule fragment merged by namespace. */
237
+ rules?: AppRules;
238
+ /** Rows created after schema/rules provisioning only when absent. */
239
+ seeds?: IntegrationSeed[];
240
+ /** Read-only unauthenticated checks run against `links.<env>`. */
241
+ probes?: IntegrationProbe[];
242
+ }
194
243
  interface ClerkAuthConfig {
195
244
  publishableKey?: string;
196
245
  audience?: string;
197
246
  mode?: "client" | "full";
198
247
  }
199
- /** Read-only event selection applied by the hosted Google calendar connector. */
200
- interface GoogleCalendarMatchConfig {
201
- /** Mirror only events whose summary begins with this value. */
202
- summaryPrefix?: string;
203
- /** Require the connected identity to organize the event. Defaults to true. */
204
- organizerSelf?: boolean;
205
- /** Require at least one attendee. Defaults to true. */
206
- requireAttendees?: boolean;
207
- }
208
- /** Google Calendar intent. OAuth credentials never belong in this config. */
248
+ /** Google Calendar booking intent. OAuth credentials never belong in this
249
+ * config, and no calendar data is mirrored — Google is the source of truth. */
209
250
  interface GoogleCalendarConfig {
210
- /** One through ten provider calendar ids to mirror in each odla environment. */
211
- calendars: Record<OdlaEnvName, string[]>;
212
- /** Public Google Appointment Schedule URL for static embed/link mode. */
251
+ /** One through ten calendars consulted for availability in each env. */
252
+ availabilityCalendars?: Record<OdlaEnvName, string[]>;
253
+ /** Retired read-mirror spelling of `availabilityCalendars` (still accepted). */
254
+ calendars?: Record<OdlaEnvName, string[]>;
255
+ /** Calendar that receives created bookings per env ("primary" or a concrete
256
+ * id). Defaults to the first availability calendar. */
257
+ bookingCalendar?: Record<OdlaEnvName, string>;
258
+ /** Optional public fallback link (retiring; first-party booking replaces it). */
213
259
  bookingPageUrl?: Record<OdlaEnvName, string | null>;
214
- match?: GoogleCalendarMatchConfig;
215
- /** Store normalized attendee addresses, or only SHA-256 match keys. */
216
- attendeePolicy?: "full" | "hashed";
217
260
  }
218
- /** The `calendar` block of `odla.config.mjs` — the Google read-mirror
219
- * settings; valid only alongside the db service. */
261
+ /** The `calendar` block of `odla.config.mjs` — the Google booking settings;
262
+ * valid only alongside the db service (booking rides the app's db key). */
220
263
  interface CalendarConfig {
221
264
  google: GoogleCalendarConfig;
222
265
  }
223
266
  /** Canonical, environment-specific config sent to the calendar controller. */
224
267
  interface CalendarServiceConfig extends Record<string, unknown> {
225
268
  provider: "google";
226
- access: "read";
227
- /** Deduplicated provider ids; the hosted connector accepts at most ten. */
228
- calendars: string[];
229
- match: {
230
- organizerSelf: boolean;
231
- requireAttendees: boolean;
232
- summaryPrefix?: string;
233
- };
234
- attendeePolicy: "full" | "hashed";
269
+ access: "book";
270
+ /** Calendar that receives created bookings. */
271
+ bookingCalendarId: string;
272
+ /** Deduplicated provider ids; the booking proxy accepts at most ten. */
273
+ availabilityCalendars: string[];
235
274
  }
236
275
  /** Authored `odla.config.mjs` contract consumed by doctor and provision. */
237
276
  interface OdlaProjectConfig {
@@ -243,6 +282,9 @@ interface OdlaProjectConfig {
243
282
  };
244
283
  envs?: OdlaEnvName[];
245
284
  services?: string[];
285
+ /** App capabilities composed into schema/rules/seeds. Integrations are npm
286
+ * modules, not hosted platform services, so they do not belong in services. */
287
+ integrations?: OdlaIntegration[];
246
288
  db?: {
247
289
  /** Inline SerializedSchema, JSON file, or JS module path exporting default/schema. */
248
290
  schema?: unknown;
@@ -325,6 +367,11 @@ interface ProvisionOptions {
325
367
  /** true forces browser launch, false disables it, undefined auto-opens whenever
326
368
  * this machine can show a browser (skipped in CI, over SSH, or with no display). */
327
369
  open?: boolean;
370
+ /** Cap the handshake-approval wait, in seconds. When it elapses the command
371
+ * exits with the pending handshake persisted; rerunning resumes it. Defaults
372
+ * to the handshake's own expiry on an interactive terminal and 90 seconds
373
+ * everywhere else (agent tool shells kill long-running commands). */
374
+ wait?: number;
328
375
  /** Test/embedding override for opening the approval URL. Defaults to the OS browser opener. */
329
376
  openApprovalUrl?: (url: string) => Promise<void>;
330
377
  /** Test/embedding override for calendar connection polling. */
@@ -346,6 +393,8 @@ interface ProvisionPlan {
346
393
  dbEndpoint: string;
347
394
  envs: string[];
348
395
  services: string[];
396
+ /** Configured app-capability ids in provisioning order. */
397
+ integrations: string[];
349
398
  hasSchema: boolean;
350
399
  hasRules: boolean;
351
400
  aiProvider?: string;
@@ -364,8 +413,8 @@ interface SmokeOptions {
364
413
  stdout?: Pick<typeof console, "log" | "error">;
365
414
  }
366
415
 
367
- /** The only Google OAuth scope the platform ever requests: read-only calendar events. */
368
- declare const GOOGLE_CALENDAR_READ_SCOPE = "https://www.googleapis.com/auth/calendar.events.readonly";
416
+ /** The Google OAuth scope that authorizes booking writes (events read/write). */
417
+ declare const GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
369
418
  /** Resolve the checked-in calendar block into one controller-safe env payload. */
370
419
  declare function calendarServiceConfig(cfg: LoadedProjectConfig, env: string): CalendarServiceConfig;
371
420
  /** Public Appointment Schedule URL to apply separately from connector config. */
@@ -378,13 +427,14 @@ interface DoctorOptions {
378
427
  /**
379
428
  * Validate and summarize an odla project config entirely offline — no network
380
429
  * calls and no file writes. Loads `configPath`, builds the provision plan, and
381
- * resolves the schema and rules (synthesizing deny-all rules from the schema
382
- * when `db.rules` is omitted and `db.defaultRules` isn't `false`), then prints a
383
- * summary line for the app, envs, services, schema entity count, rules namespace
384
- * count, and AI provider.
430
+ * resolves and collision-checks app plus integration schema/rules (synthesizing
431
+ * deny-all rules when `db.rules` is omitted and `db.defaultRules` isn't
432
+ * `false`), then prints the app, envs, services, integrations, schema entity
433
+ * count, rules namespace count, and AI provider.
385
434
  *
386
435
  * It then collects warnings for the mistakes provision can't fix silently: a
387
- * schema with no entities, a rules namespace with no matching entity, the `ai`
436
+ * schema with no entities, a rules namespace with no matching entity, an
437
+ * integration with incomplete rules or a seed targeting an unknown namespace, the `ai`
388
438
  * service enabled without an `ai.provider`, `auth.clerk` entries that reference
389
439
  * an unset env var (`$FOO`), an `ai.keyEnv` that isn't set (provider key upload
390
440
  * will be skipped), and — when `o11y` is enabled — any env whose local
@@ -442,8 +492,9 @@ declare function initProject(options: InitOptions): void;
442
492
  * developer token, creates the registry app when absent, enables each service
443
493
  * per env (AI also gets `setAi` with the configured provider/model), configures
444
494
  * Clerk auth and links, then per env mints or reuses credentials only for the
445
- * configured services, pushes the schema with the db key and the
446
- * rules with the developer token, and stores the provider key in the tenant
495
+ * configured services, collision-checks and merges integration schema/rules,
496
+ * pushes the composed schema with the db key and rules with the developer
497
+ * token, creates natural-key integration seeds only when absent, and stores the provider key in the tenant
447
498
  * vault when `ai.keyEnv` is set and present. Writes the credentials file
448
499
  * (mode 0600, gitignored) and, when requested, `.dev.vars`. Google Calendar
449
500
  * consent runs last, per env; when the platform connector itself is not yet
@@ -790,9 +841,10 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
790
841
  *
791
842
  * It then makes real network calls: fetches the registry public-config (and, when
792
843
  * `ai.provider` is configured, asserts the live provider matches), fetches the
793
- * live db schema with the db key and asserts every entity from the local schema
794
- * is present, and runs a `count` aggregate against the first entity to confirm
795
- * the db answers queries. Progress lines are printed and it ends with `ok`.
844
+ * live db schema with the db key and asserts every entity from the composed app
845
+ * plus integration schema is present, runs a `count` aggregate against the
846
+ * first entity, and checks each integration's deployed route probe anonymously
847
+ * against `links.<env>`. Progress lines are printed and it ends with `ok`.
796
848
  * Read-only — it writes nothing and throws on the first mismatch or failed
797
849
  * request.
798
850
  *
@@ -803,4 +855,4 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
803
855
  */
804
856
  declare function smoke(options: SmokeOptions): Promise<void>;
805
857
 
806
- export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, type AvailableGoogleCalendar, CAPABILITIES, type CalendarConfig, type CalendarConnectionState, type CalendarLifecycleOptions, type CalendarServiceConfig, type CalendarStatus, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, GOOGLE_CALENDAR_READ_SCOPE, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type GoogleCalendarConfig, type GoogleCalendarMatchConfig, 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, calendarBookingPageUrl, calendarCalendars, calendarConnect, calendarDisconnect, calendarResync, calendarServiceConfig, calendarStatus, 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 };
858
+ export { AGENT_HARNESSES, type AdminAiOptions, type AgentHarness, type AgentHarnessSelector, type AvailableGoogleCalendar, CAPABILITIES, type CalendarConfig, type CalendarConnectionState, type CalendarLifecycleOptions, type CalendarServiceConfig, type CalendarStatus, type CliDependencies, type ConnectGitHubSecuritySourceOptions, type DisconnectGitHubSecuritySourceOptions, type FollowHostedSecurityJobOptions, GOOGLE_CALENDAR_EVENTS_SCOPE, type GetHostedSecurityIntentOptions, type GetHostedSecurityJobOptions, type GoogleCalendarConfig, type HarnessInstallResult, type HostedSecurityProfile, type HostedSecurityResult, type HostedSecurityTokenRequest, type IntegrationProbe, type IntegrationSeed, type ListGitHubSecuritySourcesOptions, type ListHostedSecurityJobsOptions, type LoadedProjectConfig, type LocalCredentials, type OdlaIntegration, 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, calendarBookingPageUrl, calendarCalendars, calendarConnect, calendarDisconnect, calendarServiceConfig, calendarStatus, connectGitHubSecuritySource, disconnectGitHubSecuritySource, doctor, exitCodeFor, followHostedSecurityJob, getHostedSecurityIntent, getHostedSecurityJob, getHostedSecurityPlan, getHostedSecurityReport, getScopedPlatformToken, inferGitHubRepository, initProject, installSkill, isTerminalHostedSecurityStatus, listGitHubSecuritySources, listHostedSecurityJobs, printCapabilities, provision, redactSecrets, repositoryFromGitRemote, runCli, runHostedSecurity, secretsPush, secretsSet, secretsSetClerkKey, smoke, startHostedSecurityJob };