@odla-ai/cli 0.11.1 → 0.12.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,7 +99,7 @@ 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"];
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", "push db schema/rules 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 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
103
  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"];
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"];
@@ -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 {
@@ -196,42 +200,32 @@ interface ClerkAuthConfig {
196
200
  audience?: string;
197
201
  mode?: "client" | "full";
198
202
  }
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. */
203
+ /** Google Calendar booking intent. OAuth credentials never belong in this
204
+ * config, and no calendar data is mirrored — Google is the source of truth. */
209
205
  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. */
206
+ /** One through ten calendars consulted for availability in each env. */
207
+ availabilityCalendars?: Record<OdlaEnvName, string[]>;
208
+ /** Retired read-mirror spelling of `availabilityCalendars` (still accepted). */
209
+ calendars?: Record<OdlaEnvName, string[]>;
210
+ /** Calendar that receives created bookings per env ("primary" or a concrete
211
+ * id). Defaults to the first availability calendar. */
212
+ bookingCalendar?: Record<OdlaEnvName, string>;
213
+ /** Optional public fallback link (retiring; first-party booking replaces it). */
213
214
  bookingPageUrl?: Record<OdlaEnvName, string | null>;
214
- match?: GoogleCalendarMatchConfig;
215
- /** Store normalized attendee addresses, or only SHA-256 match keys. */
216
- attendeePolicy?: "full" | "hashed";
217
215
  }
218
- /** The `calendar` block of `odla.config.mjs` — the Google read-mirror
219
- * settings; valid only alongside the db service. */
216
+ /** The `calendar` block of `odla.config.mjs` — the Google booking settings;
217
+ * valid only alongside the db service (booking rides the app's db key). */
220
218
  interface CalendarConfig {
221
219
  google: GoogleCalendarConfig;
222
220
  }
223
221
  /** Canonical, environment-specific config sent to the calendar controller. */
224
222
  interface CalendarServiceConfig extends Record<string, unknown> {
225
223
  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";
224
+ access: "book";
225
+ /** Calendar that receives created bookings. */
226
+ bookingCalendarId: string;
227
+ /** Deduplicated provider ids; the booking proxy accepts at most ten. */
228
+ availabilityCalendars: string[];
235
229
  }
236
230
  /** Authored `odla.config.mjs` contract consumed by doctor and provision. */
237
231
  interface OdlaProjectConfig {
@@ -325,6 +319,11 @@ interface ProvisionOptions {
325
319
  /** true forces browser launch, false disables it, undefined auto-opens whenever
326
320
  * this machine can show a browser (skipped in CI, over SSH, or with no display). */
327
321
  open?: boolean;
322
+ /** Cap the handshake-approval wait, in seconds. When it elapses the command
323
+ * exits with the pending handshake persisted; rerunning resumes it. Defaults
324
+ * to the handshake's own expiry on an interactive terminal and 90 seconds
325
+ * everywhere else (agent tool shells kill long-running commands). */
326
+ wait?: number;
328
327
  /** Test/embedding override for opening the approval URL. Defaults to the OS browser opener. */
329
328
  openApprovalUrl?: (url: string) => Promise<void>;
330
329
  /** Test/embedding override for calendar connection polling. */
@@ -364,8 +363,8 @@ interface SmokeOptions {
364
363
  stdout?: Pick<typeof console, "log" | "error">;
365
364
  }
366
365
 
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";
366
+ /** The Google OAuth scope that authorizes booking writes (events read/write). */
367
+ declare const GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
369
368
  /** Resolve the checked-in calendar block into one controller-safe env payload. */
370
369
  declare function calendarServiceConfig(cfg: LoadedProjectConfig, env: string): CalendarServiceConfig;
371
370
  /** Public Appointment Schedule URL to apply separately from connector config. */
@@ -803,4 +802,4 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
803
802
  */
804
803
  declare function smoke(options: SmokeOptions): Promise<void>;
805
804
 
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 };
805
+ 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 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, 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,7 +99,7 @@ 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"];
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", "push db schema/rules 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 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
103
  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"];
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"];
@@ -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 {
@@ -196,42 +200,32 @@ interface ClerkAuthConfig {
196
200
  audience?: string;
197
201
  mode?: "client" | "full";
198
202
  }
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. */
203
+ /** Google Calendar booking intent. OAuth credentials never belong in this
204
+ * config, and no calendar data is mirrored — Google is the source of truth. */
209
205
  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. */
206
+ /** One through ten calendars consulted for availability in each env. */
207
+ availabilityCalendars?: Record<OdlaEnvName, string[]>;
208
+ /** Retired read-mirror spelling of `availabilityCalendars` (still accepted). */
209
+ calendars?: Record<OdlaEnvName, string[]>;
210
+ /** Calendar that receives created bookings per env ("primary" or a concrete
211
+ * id). Defaults to the first availability calendar. */
212
+ bookingCalendar?: Record<OdlaEnvName, string>;
213
+ /** Optional public fallback link (retiring; first-party booking replaces it). */
213
214
  bookingPageUrl?: Record<OdlaEnvName, string | null>;
214
- match?: GoogleCalendarMatchConfig;
215
- /** Store normalized attendee addresses, or only SHA-256 match keys. */
216
- attendeePolicy?: "full" | "hashed";
217
215
  }
218
- /** The `calendar` block of `odla.config.mjs` — the Google read-mirror
219
- * settings; valid only alongside the db service. */
216
+ /** The `calendar` block of `odla.config.mjs` — the Google booking settings;
217
+ * valid only alongside the db service (booking rides the app's db key). */
220
218
  interface CalendarConfig {
221
219
  google: GoogleCalendarConfig;
222
220
  }
223
221
  /** Canonical, environment-specific config sent to the calendar controller. */
224
222
  interface CalendarServiceConfig extends Record<string, unknown> {
225
223
  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";
224
+ access: "book";
225
+ /** Calendar that receives created bookings. */
226
+ bookingCalendarId: string;
227
+ /** Deduplicated provider ids; the booking proxy accepts at most ten. */
228
+ availabilityCalendars: string[];
235
229
  }
236
230
  /** Authored `odla.config.mjs` contract consumed by doctor and provision. */
237
231
  interface OdlaProjectConfig {
@@ -325,6 +319,11 @@ interface ProvisionOptions {
325
319
  /** true forces browser launch, false disables it, undefined auto-opens whenever
326
320
  * this machine can show a browser (skipped in CI, over SSH, or with no display). */
327
321
  open?: boolean;
322
+ /** Cap the handshake-approval wait, in seconds. When it elapses the command
323
+ * exits with the pending handshake persisted; rerunning resumes it. Defaults
324
+ * to the handshake's own expiry on an interactive terminal and 90 seconds
325
+ * everywhere else (agent tool shells kill long-running commands). */
326
+ wait?: number;
328
327
  /** Test/embedding override for opening the approval URL. Defaults to the OS browser opener. */
329
328
  openApprovalUrl?: (url: string) => Promise<void>;
330
329
  /** Test/embedding override for calendar connection polling. */
@@ -364,8 +363,8 @@ interface SmokeOptions {
364
363
  stdout?: Pick<typeof console, "log" | "error">;
365
364
  }
366
365
 
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";
366
+ /** The Google OAuth scope that authorizes booking writes (events read/write). */
367
+ declare const GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
369
368
  /** Resolve the checked-in calendar block into one controller-safe env payload. */
370
369
  declare function calendarServiceConfig(cfg: LoadedProjectConfig, env: string): CalendarServiceConfig;
371
370
  /** Public Appointment Schedule URL to apply separately from connector config. */
@@ -803,4 +802,4 @@ declare function installSkill(options?: SkillInstallOptions): SkillInstallResult
803
802
  */
804
803
  declare function smoke(options: SmokeOptions): Promise<void>;
805
804
 
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 };
805
+ 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 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, 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.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  AGENT_HARNESSES,
4
4
  CAPABILITIES,
5
- GOOGLE_CALENDAR_READ_SCOPE,
5
+ GOOGLE_CALENDAR_EVENTS_SCOPE,
6
6
  SYSTEM_AI_PURPOSES,
7
7
  adminAi,
8
8
  calendarBookingPageUrl,
9
9
  calendarCalendars,
10
10
  calendarConnect,
11
11
  calendarDisconnect,
12
- calendarResync,
13
12
  calendarServiceConfig,
14
13
  calendarStatus,
15
14
  connectGitHubSecuritySource,
16
15
  disconnectGitHubSecuritySource,
17
16
  doctor,
17
+ exitCodeFor,
18
18
  followHostedSecurityJob,
19
19
  getHostedSecurityIntent,
20
20
  getHostedSecurityJob,
@@ -38,23 +38,23 @@ import {
38
38
  secretsSetClerkKey,
39
39
  smoke,
40
40
  startHostedSecurityJob
41
- } from "./chunk-DC4MIB4X.js";
41
+ } from "./chunk-Y4HWUEFF.js";
42
42
  export {
43
43
  AGENT_HARNESSES,
44
44
  CAPABILITIES,
45
- GOOGLE_CALENDAR_READ_SCOPE,
45
+ GOOGLE_CALENDAR_EVENTS_SCOPE,
46
46
  SYSTEM_AI_PURPOSES,
47
47
  adminAi,
48
48
  calendarBookingPageUrl,
49
49
  calendarCalendars,
50
50
  calendarConnect,
51
51
  calendarDisconnect,
52
- calendarResync,
53
52
  calendarServiceConfig,
54
53
  calendarStatus,
55
54
  connectGitHubSecuritySource,
56
55
  disconnectGitHubSecuritySource,
57
56
  doctor,
57
+ exitCodeFor,
58
58
  followHostedSecurityJob,
59
59
  getHostedSecurityIntent,
60
60
  getHostedSecurityJob,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/cli",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "Agent-operable CLI for odla provisioning, calendar consent and sync lifecycle, System AI administration, Worker secrets, security jobs, and smoke checks.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://odla.ai/docs/packages/cli",
@@ -2,7 +2,7 @@
2
2
  name: odla
3
3
  description: >
4
4
  Build a new app on odla — a realtime database (odla-db) plus AI, Clerk auth,
5
- read-only Google Calendar mirrors, and observability on Cloudflare, provisioned by @odla-ai/cli. Use when the user
5
+ live Google Calendar booking, and observability on Cloudflare, provisioned by @odla-ai/cli. Use when the user
6
6
  wants to create/scaffold an odla app, add odla-db/ai/auth/o11y to a repo, or
7
7
  "get started with odla". For moving an existing static or GitHub Pages site to
8
8
  odla, use the odla-migrate skill instead.
@@ -15,7 +15,7 @@ runbookOrder:
15
15
 
16
16
  You drive setup and building on **odla** — an agent-operable app platform on
17
17
  Cloudflare: a realtime graph database (**odla-db**), multi-provider **AI**,
18
- **Clerk** auth, read-only **Google Calendar** mirrors, and OpenTelemetry **observability**, all provisioned by the
18
+ **Clerk** auth, live **Google Calendar** booking, and OpenTelemetry **observability**, all provisioned by the
19
19
  `@odla-ai/cli`. The human approves a couple of browser-only checkpoints; you do
20
20
  everything else.
21
21
 
@@ -53,7 +53,30 @@ State which path you're taking and what you'll build in one line; get a nod.
53
53
  explicitly asks.
54
54
  5. Google OAuth is a distinct human checkpoint. Never request, paste, print, or
55
55
  store a Google authorization code/refresh token. Open only the state-bound
56
- platform/Google consent URL returned by odla; calendar actions remain unavailable in the read-only slice.
56
+ platform/Google consent URL returned by odla; booking runs server-side
57
+ through the SDK with the app's existing key, never from a browser.
58
+
59
+ ## Tooling sources
60
+
61
+ - **odla**: this skill plus the installed `@odla-ai/*` packages are the
62
+ complete, offline contract (README + exported declarations/JSDoc, rendered
63
+ at `https://odla.ai/docs/packages/<pkg>`). Never reconstruct odla behavior
64
+ from the web or training memory.
65
+ - **Third-party tools (wrangler, Clerk) — the reverse**: their CLIs and config
66
+ formats evolve, so never work from memorized setup steps. When you reach a
67
+ wrangler or Clerk step, fetch the vendor's current agent docs and follow
68
+ those:
69
+ - Cloudflare: docs index `https://developers.cloudflare.com/llms.txt`; every
70
+ docs page has a markdown twin at `<page>/index.md` (wrangler config:
71
+ `https://developers.cloudflare.com/workers/wrangler/configuration/index.md`).
72
+ With the human's OK (it edits global agent config), you may also follow
73
+ `https://developers.cloudflare.com/agent-setup/prompt.md` to add
74
+ Cloudflare's own skills + MCP servers to your harness.
75
+ - Clerk: agent-first CLI setup `https://clerk.com/docs/cli.md`; docs index
76
+ `https://clerk.com/docs/llms.txt`.
77
+
78
+ Offline? Say so, continue with the vendor steps written in these references,
79
+ and flag that they may be stale.
57
80
 
58
81
  ## The flow
59
82
 
@@ -72,12 +95,18 @@ guessing which platform/credential steps need manual work. Then:
72
95
  `npx @odla-ai/cli provision --email <existing-odla-account>
73
96
  --write-dev-vars --push-secrets`. A device code prints; that same account
74
97
  signs in, reviews the exact code, and approves it at https://odla.ai/studio.
75
- Opening the link alone is inert. It creates the app,
98
+ Opening the link alone is inert. Relay the printed approval URL to the
99
+ human verbatim — the automatic browser launch is best-effort and may show
100
+ no tab. Outside an interactive terminal the wait is capped (90s default,
101
+ `--wait <seconds>`); exit code 75 means still pending: the handshake is
102
+ persisted, so once the human approves, re-run the same command and it
103
+ resumes the same code and collects the token. It creates the app,
76
104
  enables services, issues or reuses configured credentials (db key + o11y
77
105
  ingest token when enabled), pushes schema + rules, writes `.dev.vars`, and
78
106
  transfers Worker secrets through Wrangler stdin. With calendar enabled it
79
- then prints a second, server-issued Google URL; the human grants read-only consent
80
- and the CLI follows initial sync.
107
+ then prints a second, server-issued Google URL; the human grants booking
108
+ consent in a browser and the connection is immediately live — nothing
109
+ syncs.
81
110
  4. **run** — `npx wrangler dev` (auto-loads `.dev.vars`); verify locally.
82
111
  5. **security** — run the passive `@odla-ai/security` odla profile; inspect
83
112
  every lead and keep critical candidate gating enabled. If the human approves
@@ -98,10 +127,11 @@ guessing which platform/credential steps need manual work. Then:
98
127
 
99
128
  `npx @odla-ai/cli doctor` is an offline config check anytime;
100
129
  `npx @odla-ai/cli smoke --env dev` verifies a live deployment.
101
- Use `calendar status --json` for safe connection/sync state and `calendar
102
- calendars --json` to discover ids after consent; update checked-in config and
103
- re-provision. Resync production only with `--yes`; disconnect always requires
104
- `--yes` and leaves retained rows potentially stale.
130
+ Use `calendar status --json` for safe connection state (`bookable`, booking
131
+ and availability calendars) and `calendar calendars --json` to discover ids
132
+ after consent; update checked-in config and re-provision. Disconnect always
133
+ requires `--yes` and removes only this connection's sealed grant — Google
134
+ keeps every booked event.
105
135
 
106
136
  The CLI owns deterministic platform work from `odla.config.mjs`: service
107
137
  enablement, credential issuance/storage, `.dev.vars`, and Wrangler secret
@@ -15,25 +15,29 @@ deny-all rules from the schema. To turn on auth/observability, add `"o11y"` to
15
15
  `services` and an `auth.clerk` block in `odla.config.mjs` (init leaves it
16
16
  commented).
17
17
 
18
- For a Google booking mirror, include both `"db"` and `"calendar"`. Configure
19
- `calendar.google.calendars` for every env (start with `"primary"`), optional
20
- match filters, and `attendeePolicy: "full" | "hashed"`. This release is
21
- read-only; do not add OAuth credentials or action scopes to config.
22
- For static Appointment Schedule parity, add the public per-env
23
- `bookingPageUrl`; it is link/embed configuration, never a credential.
18
+ For Google Calendar booking, include both `"db"` and `"calendar"`. Configure
19
+ `calendar.google.availabilityCalendars` for every env (start with
20
+ `"primary"`; 1–10 per env) and optionally `bookingCalendar` (defaults to the
21
+ first availability calendar). Do not add OAuth credentials to config the
22
+ Google grant is platform-side. A legacy per-env `bookingPageUrl` link may be
23
+ kept as a fallback; it is public configuration, never a credential.
24
24
 
25
25
  ## 2. Build the Worker shell
26
26
 
27
27
  `npm i` only the SDKs you need and use the local `references/sdks.md` as the
28
- integration map. Create the Worker entrypoint and `wrangler.jsonc` before any
29
- deployed-secret transfer. With o11y, install `@odla-ai/o11y`, add
30
- `"nodejs_compat"` to `compatibility_flags`, and wrap the entrypoint with
31
- `withObservability`.
28
+ integration map. `npm i -D wrangler` so the project pins its own copy instead
29
+ of relying on an implicit `npx` fetch. Create the Worker entrypoint and
30
+ `wrangler.jsonc` before any deployed-secret transfer — take the current config
31
+ syntax from Cloudflare's live docs (SKILL.md "Tooling sources"), not memory.
32
+ With o11y, install `@odla-ai/o11y`, add `"nodejs_compat"` to
33
+ `compatibility_flags`, and wrap the entrypoint with `withObservability`.
32
34
 
33
35
  Each installed package's README and exported TypeScript declarations/JSDoc are
34
36
  the version-matched API contract; resolve public entry points through its
35
- `package.json` `exports`. The npm artifacts contain everything required for
36
- this flow; private service source or online documentation is not setup context.
37
+ `package.json` `exports`. For everything odla, the npm artifacts contain the
38
+ whole setup context — private service source or web documentation is never
39
+ required. Third-party tooling (wrangler, Clerk) is the reverse: use the live
40
+ vendor docs, never memorized steps.
37
41
 
38
42
  ## 3. Provision ⏸ device-code approval
39
43
 
@@ -54,11 +58,13 @@ anything unset. `--push-secrets` preflights the Wrangler config and login before
54
58
  issuing or rotating a shown-once credential.
55
59
 
56
60
  Calendar adds a second ⏸ checkpoint after the odla device approval: provision
57
- prints/opens a state-bound Google URL issued by the platform and waits while the human grants
58
- `calendar.events.readonly` and the hosted connector performs initial sync. The
59
- CLI never receives the Google callback code or tokens. Once connected, run
61
+ prints/opens a state-bound Google URL issued by the platform and waits while
62
+ the human grants the booking scopes in a browser. The CLI never receives the
63
+ Google callback code or tokens, and nothing syncs afterward — Google stays
64
+ the single source of truth. Once connected, run
60
65
  `npx @odla-ai/cli calendar calendars --env dev --json`, refine the checked-in
61
- calendar ids if needed, re-provision, and verify `calendar status --json`.
66
+ calendar ids if needed, re-provision, and verify `calendar status --env dev`
67
+ reports `bookable: yes`.
62
68
 
63
69
  The CLI stops at the source boundary: it verifies but does not invent your
64
70
  application semantics. Do not use Studio to mint a routine o11y token. Manual
@@ -78,10 +84,11 @@ means the namespace has no rule yet — add one in `src/odla/rules.mjs`
78
84
  (deliberately; e.g. `{ notes: { view: "auth.signedIn", create: "auth.signedIn" } }`),
79
85
  re-provision to push it, and retry.
80
86
 
81
- Calendar uses the same `ODLA_ENDPOINT`, `ODLA_TENANT`, and server-only
82
- `ODLA_API_KEY`; it adds no Worker secret. Keep `initCalendar` in trusted Worker
83
- code. Browser code uses db subscriptions under explicit rules or pure
84
- `@odla-ai/calendar/client` helpers, never the admin key.
87
+ Calendar rides the server-only `ODLA_API_KEY`; it adds no Worker secret. Keep
88
+ `initCalendar` in trusted Worker code and expose the app's own slots/booking
89
+ endpoints to the browser never call the platform calendar routes from
90
+ browser code. Browsers may import only the pure `@odla-ai/calendar/client`
91
+ helpers (`computeBookableSlots`, formatters), never the admin key.
85
92
 
86
93
  ## 5. Security preflight
87
94