slotlock 0.2.0 → 0.5.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +582 -0
  2. package/CONTRIBUTING.md +35 -8
  3. package/GOVERNANCE.md +4 -2
  4. package/MAINTAINERS.md +3 -2
  5. package/NOTICE +4 -4
  6. package/README.md +498 -65
  7. package/SECURITY.md +31 -2
  8. package/SPEC.md +142 -15
  9. package/agent-server.d.ts +62 -10
  10. package/agent-server.js +577 -105
  11. package/agent-store-backend.d.ts +12 -0
  12. package/agent-store-backend.js +1115 -55
  13. package/api-keys.d.ts +11 -3
  14. package/api-keys.js +29 -6
  15. package/application-api.d.ts +35 -1
  16. package/application-api.js +683 -12
  17. package/booking-rules.d.ts +84 -0
  18. package/booking-rules.js +149 -0
  19. package/config.d.ts +112 -0
  20. package/config.js +225 -0
  21. package/dashboard.d.ts +67 -5
  22. package/dashboard.js +1012 -140
  23. package/ddl.d.ts +45 -3
  24. package/ddl.js +1124 -13
  25. package/engine.d.ts +5 -0
  26. package/engine.js +23 -6
  27. package/event-feed.d.ts +178 -0
  28. package/event-feed.js +435 -0
  29. package/event-schema.d.ts +59 -0
  30. package/event-schema.js +85 -0
  31. package/ical-worker-thread.js +4 -2
  32. package/ical-worker.d.ts +8 -5
  33. package/ical-worker.js +9 -2
  34. package/index.d.ts +12 -6
  35. package/index.js +9 -4
  36. package/interop.d.ts +117 -0
  37. package/interop.js +356 -0
  38. package/maintenance.d.ts +27 -5
  39. package/maintenance.js +92 -28
  40. package/mcp-app.d.ts +7 -1
  41. package/mcp-app.js +259 -40
  42. package/metrics.d.ts +24 -0
  43. package/metrics.js +167 -0
  44. package/node-server.d.ts +9 -1
  45. package/node-server.js +7 -2
  46. package/openapi.json +10970 -0
  47. package/package.json +3 -2
  48. package/rate-limit.d.ts +80 -0
  49. package/rate-limit.js +741 -0
  50. package/rest-api.d.ts +22 -0
  51. package/rest-api.js +166 -11
  52. package/rules.js +6 -3
  53. package/self-host.d.ts +45 -8
  54. package/self-host.js +616 -46
  55. package/slotlock-manifest.schema.json +7 -1
  56. package/slotlock.manifest.json +8 -2
  57. package/store.d.ts +647 -28
  58. package/store.js +2982 -327
  59. package/sync.d.ts +43 -2
  60. package/sync.js +1515 -323
  61. package/timezone.d.ts +58 -3
  62. package/timezone.js +249 -20
  63. package/types.d.ts +300 -9
  64. package/types.js +0 -2
  65. package/webhooks.d.ts +91 -0
  66. package/webhooks.js +300 -0
package/dashboard.d.ts CHANGED
@@ -1,9 +1,14 @@
1
1
  import type { SlotlockApiKeyStore } from './api-keys.js';
2
- import type { SlotlockSql, SlotlockStore } from './store.js';
3
- import type { SlotlockResource, WeeklyAvailabilityRule } from './types.js';
2
+ import { type SlotlockBookingRules } from './booking-rules.js';
3
+ import { type CreateWebhookEndpointResult, type SlotlockChange, type SlotlockWebhookEndpoint } from './event-feed.js';
4
+ import type { SlotlockSql, SlotlockStore, SlotlockWaitlistEntry } from './store.js';
5
+ import type { Interval, SlotlockAgendaEntry, SlotlockResource, WeeklyAvailabilityRule } from './types.js';
4
6
  /** Resources one dashboard tenant may hold. */
5
7
  export declare const SLOTLOCK_DASHBOARD_MAX_RESOURCES = 100;
6
- /** A tenant's calendar resources, as the dashboard lists and adds them. */
8
+ /**
9
+ * A tenant's calendar resources, as the dashboard lists and adds them. Each write takes, last, the
10
+ * person who made it (`github:<user id>`) as `actor`, to record on the event feed.
11
+ */
7
12
  export interface SlotlockDashboardResources {
8
13
  list(tenantRef: string): Promise<SlotlockResource[]>;
9
14
  /**
@@ -11,7 +16,7 @@ export interface SlotlockDashboardResources {
11
16
  * (an error with `code: 'resource_limit_reached'`) once the tenant has
12
17
  * SLOTLOCK_DASHBOARD_MAX_RESOURCES, however many adds race, on however many servers.
13
18
  */
14
- add(tenantRef: string, externalRef: string, timezone: string): Promise<SlotlockResource>;
19
+ add(tenantRef: string, externalRef: string, timezone: string, actor?: string): Promise<SlotlockResource>;
15
20
  /**
16
21
  * Set the bookable hours of one of the tenant's resources (`{ id }`) or of all of them; `null`
17
22
  * hands them back to the server's and `[]` closes. Returns how many resources changed: 0 for an
@@ -19,7 +24,32 @@ export interface SlotlockDashboardResources {
19
24
  */
20
25
  setHours(tenantRef: string, target: {
21
26
  id: string;
22
- } | 'all', rules: WeeklyAvailabilityRule[] | null): Promise<number>;
27
+ } | 'all', rules: WeeklyAvailabilityRule[] | null, actor?: string): Promise<number>;
28
+ /** What occupies the tenant's resources in `window`, by start: the overview's agenda. */
29
+ agenda?(tenantRef: string, window: Interval, limit: number): Promise<SlotlockAgendaEntry[]>;
30
+ /** The tenant's latest changes, newest first: the overview's activity. */
31
+ recentChanges?(tenantRef: string, limit: number): Promise<SlotlockChange[]>;
32
+ /**
33
+ * Set the booking rules of one of the tenant's resources (null clears them): the resource, or
34
+ * null when it is not the tenant's. Invalid rules throw with `code: 'invalid_booking_rules'`.
35
+ */
36
+ setRules?(tenantRef: string, id: string, rules: SlotlockBookingRules | null, actor?: string): Promise<SlotlockResource | null>;
37
+ /** Put one of the tenant's resources in a pool, or none: as setRules; `code: 'invalid_pool'`. */
38
+ setPool?(tenantRef: string, id: string, pool: string | null, actor?: string): Promise<SlotlockResource | null>;
39
+ /** The tenant's waitlist, oldest first, and leaving it (false when the entry is not the tenant's). */
40
+ waitlist?: {
41
+ list(tenantRef: string, limit: number): Promise<SlotlockWaitlistEntry[]>;
42
+ leave(tenantRef: string, id: string, actor?: string): Promise<boolean>;
43
+ };
44
+ /** The tenant's webhook endpoints, as the event feed keeps them. */
45
+ webhooks?: {
46
+ list(tenantRef: string): Promise<SlotlockWebhookEndpoint[]>;
47
+ create(tenantRef: string, url: string, eventTypes: readonly string[] | undefined, actor?: string): Promise<CreateWebhookEndpointResult>;
48
+ remove(tenantRef: string, id: string, actor?: string): Promise<boolean>;
49
+ enable(tenantRef: string, id: string, actor?: string): Promise<SlotlockWebhookEndpoint | null | {
50
+ code: 'changes_pruned';
51
+ }>;
52
+ };
23
53
  }
24
54
  /**
25
55
  * What the dashboard records so a sign-in callback works once, a sent form runs once and a
@@ -47,6 +77,8 @@ export interface SlotlockDashboardState {
47
77
  export interface SlotlockDashboardOptions {
48
78
  /** Where people reach the server; the dashboard lives at `<publicUrl>/dashboard`. */
49
79
  publicUrl: string;
80
+ /** What the deployment is called in the dashboard: 1-60 characters. Defaults to `Slotlock`. */
81
+ name?: string;
50
82
  /**
51
83
  * Where agents reach the server's MCP and A2A endpoints, which the dashboard shows, when it is
52
84
  * served from another origin (on Cloudflare Workers, with the server elsewhere). Defaults to
@@ -63,11 +95,23 @@ export interface SlotlockDashboardOptions {
63
95
  authorizeUrl?: string;
64
96
  tokenUrl?: string;
65
97
  userUrl?: string;
98
+ membershipsUrl?: string;
66
99
  };
67
100
  /** Seals sessions and sign-in state and derives CSRF tokens: 32+ random characters. */
68
101
  sessionSecret: string;
69
102
  /** GitHub user ids (numbers, as strings) that may sign in, or `'*'` for every GitHub account. */
70
103
  allowedUsers: '*' | readonly string[];
104
+ /**
105
+ * GitHub organization ids (numbers, as strings) this dashboard serves. With them, a tenant is an
106
+ * organization (`github-org:<id>`), not a person: sign-in asks for `read:org`, and a person signs
107
+ * in to the served organizations they are an active member of, sharing each one's keys and
108
+ * resources with its other members: owners and members, not billing managers, from the first
109
+ * 100 memberships GitHub lists, up to 20 served organizations. Membership is read at each sign-in; leaving an organization
110
+ * ends access at the next sign-in, at most a session (12 hours) later, and removing an
111
+ * organization from this list signs its sessions out at once. Without it, each person is their
112
+ * own tenant (`github:<id>`).
113
+ */
114
+ organizations?: readonly string[];
71
115
  keys: SlotlockApiKeyStore;
72
116
  resources: SlotlockDashboardResources;
73
117
  /** Shared by every server behind this origin: see SlotlockDashboardState. */
@@ -76,6 +120,12 @@ export interface SlotlockDashboardOptions {
76
120
  availability?: readonly WeeklyAvailabilityRule[];
77
121
  fetch?: typeof fetch;
78
122
  now?: () => number;
123
+ /**
124
+ * The address a request came from, as the server counts its clients (the socket's, or a trusted
125
+ * proxy's field such as CF-Connecting-IP), so one client's sign-ins in flight are capped below
126
+ * every client's. Without it, or when it returns undefined, only the cap on every client applies.
127
+ */
128
+ clientAddress?: (request: Request) => string | undefined;
79
129
  /**
80
130
  * Called with whatever a request handler throws; the person sees a generic error page. Not
81
131
  * awaited; what it throws, or its promise rejects with, is ignored.
@@ -92,6 +142,18 @@ export interface SlotlockDashboard {
92
142
  * in the database (a READ COMMITTED transaction, under a per-tenant lock).
93
143
  */
94
144
  export declare function createSlotlockDashboardResources(store: SlotlockStore): SlotlockDashboardResources;
145
+ /**
146
+ * Records accepted write confirmations (`SlotlockAgentConfirmationOptions.consumeAcceptance`) in
147
+ * Postgres, shared by every server on the database: true the first time a confirmation's nonce is
148
+ * recorded, false while it stays recorded, so an accepted confirmation runs its write once on any
149
+ * server. Kept until the confirmation's own expiry, through the same function and table as the
150
+ * dashboard's records (only a SHA-256 digest of the nonce is stored); pass a client, not a
151
+ * transaction.
152
+ */
153
+ export declare function createSlotlockAcceptanceRecords(sql: SlotlockSql): (acceptance: {
154
+ nonce: string;
155
+ expiresAt: Date;
156
+ }) => Promise<boolean>;
95
157
  /**
96
158
  * The dashboard's records in Postgres, shared by every server on the database. Run `applySchema()`
97
159
  * and `grantApplicationRole()` first: the serving role reaches the records only through