slotlock 0.5.2 → 0.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slotlock",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "type": "module",
5
5
  "description": "Resource-first, deterministic calendar and availability engine for AI agents.",
6
6
  "license": "Apache-2.0",
package/self-host.js CHANGED
@@ -8,7 +8,7 @@ import { createHash, timingSafeEqual } from 'node:crypto';
8
8
  import { parseArgs } from 'node:util';
9
9
  import postgres from 'postgres';
10
10
  import { createSlotlockAgentServer, slotlockAgentOperationScope, } from './agent-server.js';
11
- import { createSlotlockStoreAgentBackend, createSlotlockStoreApplicationBackend, } from './agent-store-backend.js';
11
+ import { createSlotlockStoreAgentBackend, createSlotlockStoreApplicationBackend, createSlotlockStoreChangeSource, } from './agent-store-backend.js';
12
12
  import { SLOTLOCK_API_KEY_DEFAULT_SCOPES, SLOTLOCK_API_KEY_MAX_LIFETIME_DAYS, SLOTLOCK_API_KEY_SCOPES, createSlotlockApiKeyAuthenticator, createSlotlockApiKeyStore, slotlockBearerCredential, } from './api-keys.js';
13
13
  import { createSlotlockDashboard, createSlotlockDashboardResources, createSlotlockAcceptanceRecords, createSlotlockDashboardState, } from './dashboard.js';
14
14
  import { SECRET_HINT, SlotlockConfigError, formatSlotlockRate, optional, readAvailability, readDashboardConfig, readDashboardOrganizations, readLimits, readSecret, readServerUrl, readTrustedProxyHeader, slotlockKeySubscriptions, } from './config.js';
@@ -657,6 +657,8 @@ export async function startSlotlockServer(config, log) {
657
657
  return true;
658
658
  },
659
659
  subscriptions: {
660
+ // Live updates poll each tenant's change feed (one reader per tenant).
661
+ changes: createSlotlockStoreChangeSource(store, sql),
660
662
  maxTotal: caps.subscriptions,
661
663
  maxPerTenant: caps.tenant_subscriptions,
662
664
  maxPerPrincipal: slotlockKeySubscriptions(caps),
@@ -789,7 +791,9 @@ export async function startSlotlockServer(config, log) {
789
791
  send,
790
792
  feed: {
791
793
  claimWebhookDeliveries: (params) => deliveryStore.withTenant(tenantRef, (tenant) => tenant.claimWebhookDeliveries(params)),
792
- settleWebhookDelivery: (params) => deliveryStore.withTenant(tenantRef, (tenant) => tenant.settleWebhookDelivery(params)),
794
+ settleWebhookDelivery: (params) => deliveryStore.withTenant(tenantRef, (tenant) => tenant.settleWebhookDelivery(params), {
795
+ origin: 'maintenance',
796
+ }),
793
797
  },
794
798
  }),
795
799
  // Counts only, never the tenant or a URL.
@@ -1551,7 +1555,7 @@ export async function runSlotlockCli(argv, io) {
1551
1555
  }
1552
1556
  case 'key rotate': {
1553
1557
  const config = readSlotlockDatabaseConfig(io.env);
1554
- const rotated = await withApiKeys(config, (keys) => keys.rotate({ tenantRef: config.tenantRef, id: operands[1] }));
1558
+ const rotated = await withApiKeys(config, (keys) => keys.rotate({ tenantRef: config.tenantRef, id: operands[1], actor: CLI_KEY_CREATOR }));
1555
1559
  if (!rotated) {
1556
1560
  io.stderr.write('slotlock: no active API key with that id in this tenant\n');
1557
1561
  return 1;
@@ -1562,7 +1566,7 @@ export async function runSlotlockCli(argv, io) {
1562
1566
  }
1563
1567
  case 'key revoke': {
1564
1568
  const config = readSlotlockDatabaseConfig(io.env);
1565
- const revoked = await withApiKeys(config, (keys) => keys.revoke({ tenantRef: config.tenantRef, id: operands[1] }));
1569
+ const revoked = await withApiKeys(config, (keys) => keys.revoke({ tenantRef: config.tenantRef, id: operands[1], actor: CLI_KEY_CREATOR }));
1566
1570
  if (!revoked) {
1567
1571
  io.stderr.write('slotlock: no API key with that id in this tenant\n');
1568
1572
  return 1;
@@ -3,7 +3,7 @@
3
3
  "schemaVersion": "1.0",
4
4
  "name": "Slotlock",
5
5
  "package": "slotlock",
6
- "version": "0.5.2",
6
+ "version": "0.5.3",
7
7
  "description": "Resource-first, deterministic calendar and availability engine for AI agents.",
8
8
  "intervalSemantics": {
9
9
  "boundary": "[)",
package/store.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { type SlotlockEventFeed } from './event-feed.js';
1
+ import { type SlotlockChangeOrigin, type SlotlockEventFeed } from './event-feed.js';
2
2
  import { type SlotlockBookingRules } from './booking-rules.js';
3
3
  import type { Sql, TransactionSql } from 'postgres';
4
4
  import { SLOTLOCK_CALENDAR_HORIZON_DAYS } from './rules.js';
5
- import type { AcquireHoldResult, CalendarCoverageCommand, CalendarCoverageResult, CalendarEventContent, CalendarEventRetentionResult, BookingRetentionResult, SlotlockTenantErasure, CalendarFreeBusy, CalendarHorizonRollCursor, CalendarHorizonRollResult, CancelCalendarEventResult, CancelExternalReservationResult, ConfirmHoldResult, CreateReservationResult, DeleteResourceResult, ExternalReservationCommand, ExternalReservationResult, Interval, SlotlockCalendarEvent, SlotlockAgendaEntry, SlotlockHold, SlotlockReservation, SlotlockResource, PutCalendarEventCommand, PutCalendarEventResult, ReleaseHoldResult, WeeklyAvailabilityRule } from './types.js';
5
+ import type { AcquireHoldResult, CalendarCoverageCommand, CalendarCoverageResult, CalendarEventContent, CalendarEventRetentionResult, BookingRetentionResult, SlotlockTenantErasure, SlotlockBookingRuleDefaults, CalendarFreeBusy, CalendarHorizonRollCursor, CalendarHorizonRollResult, CancelCalendarEventResult, CancelExternalReservationResult, ConfirmHoldResult, CreateReservationResult, DeleteResourceResult, ExternalReservationCommand, ExternalReservationResult, Interval, SlotlockCalendarEvent, SlotlockAgendaEntry, SlotlockHold, SlotlockReservation, SlotlockResource, PutCalendarEventCommand, PutCalendarEventResult, ReleaseHoldResult, WeeklyAvailabilityRule } from './types.js';
6
6
  export { SLOTLOCK_CALENDAR_HORIZON_DAYS };
7
7
  /**
8
8
  * Longest one-off event the store accepts (ten years and some leap days): long enough for any lease
@@ -90,6 +90,8 @@ export declare function legacyPutCommandHash(params: Parameters<typeof putComman
90
90
  }): string;
91
91
  /** Members a pool booking or search considers at most, in id order. */
92
92
  export declare const SLOTLOCK_MAX_POOL_MEMBERS = 100;
93
+ /** The most pools of one tenant that may have default booking rules (`setBookingRuleDefaults`). */
94
+ export declare const SLOTLOCK_MAX_POOL_RULE_DEFAULTS = 1000;
93
95
  export declare function isPoolName(value: unknown): value is string;
94
96
  export interface SlotlockStore extends SlotlockEventFeed {
95
97
  /**
@@ -148,6 +150,13 @@ export interface SlotlockStore extends SlotlockEventFeed {
148
150
  * without control characters: the history (listHistory) records it with each change.
149
151
  */
150
152
  actor?: string;
153
+ /**
154
+ * Where the changes come from (SLOTLOCK_CHANGE_ORIGINS: `mcp`, `rest`, `dashboard`, ...),
155
+ * recorded with each beside the actor. Unset, the enclosing withTenant's, else `library`.
156
+ */
157
+ origin?: SlotlockChangeOrigin;
158
+ /** The id (a UUID) of the API key making these changes, recorded with each. */
159
+ keyId?: string;
151
160
  }): Promise<T>;
152
161
  /**
153
162
  * INVARIANT (erasure completeness): a tenant integration must use a stable, erasable business
@@ -228,6 +237,24 @@ export interface SlotlockStore extends SlotlockEventFeed {
228
237
  id: string;
229
238
  rules: SlotlockBookingRules | null;
230
239
  }): Promise<SlotlockResource | null>;
240
+ /**
241
+ * Set (or clear, with `null`) the tenant's default booking rules, or one pool's (`pool`): a
242
+ * resource's rules as they apply (`effectiveBookingRules`) are the tenant's, overlaid by its
243
+ * pool's, overlaid by its own, a field set at a narrower level winning. They apply to bookings made
244
+ * from then on. `checkInMinutes` is only a resource's own (`invalid_booking_rules`, as for rules
245
+ * outside their bounds); `pool` is a pool name (`invalid_pool`). At most
246
+ * SLOTLOCK_MAX_POOL_RULE_DEFAULTS pools a tenant have them (`pool_rules_limit_reached` for one
247
+ * more). Returns the rules stored.
248
+ */
249
+ setBookingRuleDefaults(params: {
250
+ tenantRef: string;
251
+ pool: string | null;
252
+ rules: SlotlockBookingRules | null;
253
+ }): Promise<SlotlockBookingRules | null>;
254
+ /** The tenant's default booking rules, and each pool's that has them, by pool name. */
255
+ getBookingRuleDefaults(params: {
256
+ tenantRef: string;
257
+ }): Promise<SlotlockBookingRuleDefaults>;
231
258
  /**
232
259
  * Put one resource in a pool, or take it out (`null`): `pool` is 1-100 characters of a-z, 0-9,
233
260
  * `.`, `_` and `-`, starting with a letter or digit (`invalid_pool` otherwise). A resource is in
@@ -870,6 +897,8 @@ export type CheckInReservationResult = {
870
897
  /** One source's coverage of a resource, with its health (listCoverage). */
871
898
  export interface SlotlockCoverageHealth {
872
899
  source: string;
900
+ /** How the change feed names the source (slotlockCoverageSourceId), never the source itself. */
901
+ sourceId: string;
873
902
  start: Date;
874
903
  end: Date;
875
904
  revision: number;
@@ -974,4 +1003,4 @@ export declare function createSlotlockStore(sql: StoreSql, options?: SlotlockSto
974
1003
  * Every store method that writes for a tenant: what a suspended tenant's withTenant store refuses.
975
1004
  * Maintenance (rolling series, pruning, purging) and erasure are not among them.
976
1005
  */
977
- export declare const SLOTLOCK_TENANT_WRITE_METHODS: readonly ["createResource", "archiveResource", "restoreResource", "deleteResource", "setResourceAvailability", "setResourceBookingRules", "setResourcePool", "setTenantAvailability", "createReservation", "createExternalReservation", "rescheduleExternalReservation", "cancelExternalReservation", "acquireHold", "confirmHold", "releaseHold", "putCalendarEvent", "cancelCalendarEvent", "cancelCalendarEventById", "recordCalendarCoverage", "createWebhookEndpoint", "deleteWebhookEndpoint", "enableWebhookEndpoint", "recordCoverageFailure", "deleteCoverage", "checkInReservation", "joinWaitlist", "leaveWaitlist", "putAssistantSettings", "deleteAssistantSettings", "putAssistantConversation", "settleAssistantAction", "deleteAssistantConversation"];
1006
+ export declare const SLOTLOCK_TENANT_WRITE_METHODS: readonly ["createResource", "archiveResource", "restoreResource", "deleteResource", "setResourceAvailability", "setResourceBookingRules", "setBookingRuleDefaults", "setResourcePool", "setTenantAvailability", "createReservation", "createExternalReservation", "rescheduleExternalReservation", "cancelExternalReservation", "acquireHold", "confirmHold", "releaseHold", "putCalendarEvent", "cancelCalendarEvent", "cancelCalendarEventById", "recordCalendarCoverage", "createWebhookEndpoint", "deleteWebhookEndpoint", "enableWebhookEndpoint", "recordCoverageFailure", "deleteCoverage", "checkInReservation", "joinWaitlist", "leaveWaitlist", "putAssistantSettings", "deleteAssistantSettings", "putAssistantConversation", "settleAssistantAction", "deleteAssistantConversation"];