frontier-os-app-builder 1.1.0 → 1.2.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/agents/fos-executor.md +22 -65
  4. package/agents/fos-plan-checker.md +13 -12
  5. package/agents/fos-planner.md +20 -67
  6. package/agents/fos-researcher.md +14 -10
  7. package/agents/fos-verifier.md +11 -5
  8. package/bin/fos-tools.cjs +48 -11
  9. package/bin/install.js +8 -5
  10. package/commands/fos/add-feature.md +1 -2
  11. package/commands/fos/discuss.md +0 -1
  12. package/commands/fos/new-app.md +1 -3
  13. package/commands/fos/new-milestone.md +1 -1
  14. package/commands/fos/plan.md +0 -2
  15. package/package.json +7 -1
  16. package/references/app-patterns.md +46 -28
  17. package/references/deployment.md +40 -74
  18. package/references/module-index.md +32 -0
  19. package/references/sdk/chain.md +92 -0
  20. package/references/sdk/communities.md +159 -0
  21. package/references/sdk/events.md +212 -0
  22. package/references/sdk/init.md +126 -0
  23. package/references/sdk/navigation.md +49 -0
  24. package/references/sdk/offices.md +76 -0
  25. package/references/sdk/partnerships.md +111 -0
  26. package/references/sdk/storage.md +44 -0
  27. package/references/sdk/thirdparty.md +240 -0
  28. package/references/sdk/token-amount.md +99 -0
  29. package/references/sdk/types.md +27 -0
  30. package/references/sdk/ui-utils.md +39 -0
  31. package/references/sdk/user.md +208 -0
  32. package/references/sdk/wallet.md +334 -0
  33. package/references/verification-rules.md +18 -18
  34. package/templates/app/frontier-services.tsx +75 -18
  35. package/templates/app/layout.tsx +19 -9
  36. package/templates/app/package.json +2 -1
  37. package/templates/app/public/favicon.svg +3 -0
  38. package/templates/app/sdk-context.tsx +7 -9
  39. package/templates/app/sdk-services.tsx +92 -117
  40. package/templates/app/vercel.json +8 -47
  41. package/templates/state/plan.md +32 -14
  42. package/templates/state/roadmap.md +2 -2
  43. package/templates/state/summary.md +26 -29
  44. package/workflows/add-feature.md +6 -1
  45. package/workflows/discuss.md +9 -3
  46. package/workflows/execute-plan.md +3 -3
  47. package/workflows/execute.md +17 -6
  48. package/workflows/new-app.md +54 -18
  49. package/workflows/new-milestone.md +9 -2
  50. package/workflows/plan.md +14 -5
  51. package/workflows/ship.md +26 -10
  52. package/workflows/status.md +0 -1
  53. package/references/module-inference.md +0 -348
  54. package/references/sdk-surface.md +0 -1600
  55. package/templates/app/main-simple-standalone.tsx +0 -19
  56. package/templates/app/main-simple.tsx +0 -19
  57. package/templates/state/manifest.json +0 -12
@@ -0,0 +1,212 @@
1
+ # Events Module
2
+
3
+ **Trigger keywords:** event, meetup, gathering, calendar, schedule, room, booking, reserve, reservation, space, venue, location, conference, meeting, coworking
4
+
5
+ Access via `sdk.getEvents()`. Manage events, locations (event spaces and rooms), and room bookings.
6
+
7
+ ---
8
+
9
+ ## Methods
10
+
11
+ ```typescript
12
+ listEvents(payload?: ListEventsParams): Promise<PaginatedResponse<Event>>
13
+ ```
14
+ List active events with optional filters (search, type, location, date range). Permission: `events:listEvents`
15
+
16
+ ```typescript
17
+ createEvent(payload: CreateEventRequest): Promise<Event>
18
+ ```
19
+ Create a new event. Permission: `events:createEvent`
20
+
21
+ ```typescript
22
+ addEventHost(payload: { eventId: number; email: string }): Promise<Event>
23
+ ```
24
+ Add a co-host to an event. Only the primary host can add co-hosts, and only to upcoming events. Permission: `events:addEventHost`
25
+
26
+ ```typescript
27
+ listLocations(payload?: ListLocationsParams): Promise<Location[]>
28
+ ```
29
+ List available locations. Returns an array (not paginated). Optional `locationType` filter. Permission: `events:listLocations`
30
+
31
+ ```typescript
32
+ listRoomBookings(payload?: ListRoomBookingsParams): Promise<PaginatedResponse<RoomBooking>>
33
+ ```
34
+ List approved room bookings with optional filters. Permission: `events:listRoomBookings`
35
+
36
+ ```typescript
37
+ createRoomBooking(payload: CreateRoomBookingRequest): Promise<RoomBooking>
38
+ ```
39
+ Create a room booking. Location must be of type `'room'`. Permission: `events:createRoomBooking`
40
+
41
+ ```typescript
42
+ getCryptoDepositPreflight(payload: { eventId: number }): Promise<DepositPreflight>
43
+ ```
44
+ Preflight an event's FND security deposit. Host only, read-only. Returns the spender plus candidate ERC-20 tokens (iFND first, then FND) with on-chain decimals and base-unit amounts so you can approve the allowance BEFORE placing the deposit. Throws if not authenticated, not the host, or no deposit is required. Permission: `events:getCryptoDepositPreflight`
45
+
46
+ ```typescript
47
+ placeCryptoDeposit(payload: { eventId: number }): Promise<DepositResult>
48
+ ```
49
+ Place the FND security deposit. The backend `transferFrom`s the stablecoin from the member's smart account into treasury; the member must first approve the allowance to the preflight `spender` via `getWallet().approveERC20(token.address, spender, BigInt(token.baseUnits))`. Returns `status: 'secured'` (`reference` is the tx hash) or `'awaiting_payment'` (read `statusReason`, fix, retry). Host only. Permission: `events:placeCryptoDeposit`
50
+
51
+ **Canonical 3-step deposit flow** (host only):
52
+ ```typescript
53
+ // 1. Preflight — discover the spender + candidate tokens (iFND first, then FND)
54
+ const { spender, tokens } = await sdk.getEvents().getCryptoDepositPreflight({ eventId: 42 });
55
+ const token = tokens[0]; // prefer iFND; fall back to tokens[1] (FND) if the member lacks iFND
56
+ // 2. Approve the allowance to the spender (on-chain; the member confirms in the wallet)
57
+ await sdk.getWallet().approveERC20(token.address, spender, BigInt(token.baseUnits));
58
+ // 3. Place the deposit — the backend transferFroms it into treasury
59
+ const result = await sdk.getEvents().placeCryptoDeposit({ eventId: 42 });
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Types
65
+
66
+ ```typescript
67
+ type EventType = 'public' | 'members_plus_one' | 'members_only' | 'community_only';
68
+ type EventService = 'luma' | 'private' | 'test';
69
+ type ReviewStatus = 'not_required' | 'approved' | 'rejected' | 'pending';
70
+ type EventStatus = 'active' | 'suspended' | 'archived';
71
+ type LocationType = 'event_space' | 'room';
72
+
73
+ // Compact deposit status on the Event payload (host UI gating). 7 values.
74
+ type DepositStatus = 'not_required' | 'required' | 'pending' | 'secured' | 'released' | 'withheld' | 'failed';
75
+
76
+ // Raw status returned by placeCryptoDeposit. 6 values — has 'awaiting_payment' + 'grant', lacks 'not_required'/'required'/'pending'.
77
+ type CryptoDepositStatus = 'secured' | 'awaiting_payment' | 'grant' | 'released' | 'withheld' | 'failed';
78
+
79
+ interface Event {
80
+ id: number;
81
+ name: string;
82
+ description: string;
83
+ eventType: EventType;
84
+ eventService: EventService;
85
+ host: string;
86
+ community: number | null;
87
+ startsAt: string;
88
+ endsAt: string;
89
+ coverImage: string | null;
90
+ eventId: string;
91
+ location: string;
92
+ locationName: string;
93
+ displayLocation: string;
94
+ url: string;
95
+ additionalHosts: string[];
96
+ color: string;
97
+ reviewStatus: ReviewStatus;
98
+ status: EventStatus;
99
+ isHost?: boolean; // True iff the requesting user is this event's host (the user the deposit endpoints authorize)
100
+ deposit?: EventDeposit | null; // Read-only security-deposit summary, null when no deposit row
101
+ }
102
+
103
+ interface ListEventsParams {
104
+ search?: string;
105
+ eventType?: EventType;
106
+ locationType?: LocationType;
107
+ locationId?: string;
108
+ date?: string; // YYYY-MM-DD
109
+ startDate?: string; // YYYY-MM-DD
110
+ endDate?: string; // YYYY-MM-DD
111
+ page?: number;
112
+ }
113
+
114
+ interface CreateEventRequest {
115
+ name: string;
116
+ eventType: EventType;
117
+ startsAt: string; // ISO 8601
118
+ endsAt: string; // ISO 8601
119
+ location: string; // readable_id slug
120
+ description?: string;
121
+ coverImage?: string; // Base64 data URI
122
+ additionalHosts?: string[];
123
+ color?: string; // Hex color code
124
+ }
125
+
126
+ interface Location {
127
+ id: number;
128
+ owner: number | null;
129
+ readableId: string;
130
+ name: string;
131
+ maxCapacity: number;
132
+ description: string;
133
+ directions: string;
134
+ locationType: LocationType;
135
+ warmupBuffer: string; // e.g. "00:10:00"
136
+ cooldownBuffer: string; // e.g. "00:15:00"
137
+ openBooking: boolean;
138
+ floorLocation: string;
139
+ }
140
+
141
+ interface ListLocationsParams {
142
+ locationType?: LocationType;
143
+ }
144
+
145
+ interface RoomBooking {
146
+ id: number;
147
+ startsAt: string;
148
+ endsAt: string;
149
+ location: string;
150
+ }
151
+
152
+ interface ListRoomBookingsParams {
153
+ locationId?: string;
154
+ date?: string; // YYYY-MM-DD
155
+ startDate?: string; // YYYY-MM-DD
156
+ endDate?: string; // YYYY-MM-DD
157
+ page?: number;
158
+ }
159
+
160
+ interface CreateRoomBookingRequest {
161
+ startsAt: string; // ISO 8601
162
+ endsAt: string; // ISO 8601
163
+ location: string; // readable_id (must be room type)
164
+ }
165
+
166
+ interface EventDeposit {
167
+ status: DepositStatus;
168
+ amount: number; // snapshotted deposit amount in `currency` (e.g. 400 for the FND rail)
169
+ currency: string; // e.g. "usd"
170
+ }
171
+
172
+ interface DepositPreflightToken {
173
+ key: string; // e.g. "ifnd_token" or "fnd_token"
174
+ address: string; // ERC-20 contract to approve the allowance on
175
+ decimals: number; // on-chain token decimals (read from the contract, never assumed)
176
+ baseUnits: string; // deposit amount in this token's base units, as a decimal string (wrap in BigInt() for approveERC20)
177
+ }
178
+
179
+ interface DepositPreflight {
180
+ spender: string; // address to approve the allowance to (treasury) — never hardcode it
181
+ network: string; // e.g. "base" (prod) or "base_sepolia" (sandbox)
182
+ amount: string; // deposit amount in `currency`, as a decimal string (e.g. "400.00")
183
+ currency: string; // e.g. "usd"
184
+ tokens: DepositPreflightToken[]; // candidate tokens in preference order: iFND first, then FND
185
+ }
186
+
187
+ interface DepositResult {
188
+ provider: 'crypto'; // always 'crypto' on this rail
189
+ status: CryptoDepositStatus;
190
+ amount: string; // deposit amount in `currency`, as a decimal string
191
+ currency: string; // e.g. "usd"
192
+ reference: string; // on-chain tx hash when secured; empty otherwise
193
+ statusReason: string; // human-readable reason when not secured (e.g. insufficient iFND/FND allowance or balance)
194
+ }
195
+ ```
196
+
197
+ > **Deposit notes:** `DepositStatus` (7 values, carried on the `Event` payload) DIFFERS from `CryptoDepositStatus` (6 values, returned by `placeCryptoDeposit` — it adds `awaiting_payment` + `grant` and drops `not_required`/`required`/`pending`). `DepositPreflightToken.baseUnits` is a decimal STRING — wrap it via `BigInt()` for `approveERC20`. Never hardcode `spender` or token addresses, and read on-chain `decimals` rather than assuming 6.
198
+
199
+ ---
200
+
201
+ ## Permissions (8)
202
+
203
+ | Permission | Description |
204
+ |---|---|
205
+ | `events:listEvents` | List events with optional filters (paginated) |
206
+ | `events:createEvent` | Create a new event |
207
+ | `events:addEventHost` | Add a co-host to an event |
208
+ | `events:listLocations` | List available locations (event spaces and rooms) |
209
+ | `events:listRoomBookings` | List room bookings (paginated) |
210
+ | `events:createRoomBooking` | Create a room booking |
211
+ | `events:getCryptoDepositPreflight` | Preflight an event's FND security deposit (spender, candidate tokens, amounts) before approving the allowance |
212
+ | `events:placeCryptoDeposit` | Place an event's FND security deposit (backend transferFrom from the member's smart account) |
@@ -0,0 +1,126 @@
1
+ # SDK Initialization & Core Protocol
2
+
3
+ Frontier SDK v0.24.0 — `@frontiertower/frontier-sdk`
4
+
5
+ Import paths:
6
+ - `@frontiertower/frontier-sdk` -- main SDK class and access modules
7
+ - `@frontiertower/frontier-sdk/ui-utils` -- detection and standalone helpers
8
+
9
+ The package **root** also exports the bigint token-amount helpers `parseAmount`, `formatAmount`, `FND_DECIMALS`, and `InvalidAmountError` (from `@frontiertower/frontier-sdk`, **not** `/ui-utils`) — the canonical bridge for displaying and parsing the now-`bigint` FND amounts. See [`token-amount.md`](./token-amount.md).
10
+
11
+ ---
12
+
13
+ ## Class: `FrontierSDK`
14
+
15
+ ```typescript
16
+ import { FrontierSDK } from '@frontiertower/frontier-sdk';
17
+ ```
18
+
19
+ ### Constructor
20
+
21
+ ```typescript
22
+ const sdk = new FrontierSDK();
23
+ ```
24
+
25
+ On construction the SDK:
26
+ 1. Instantiates all ten access modules (wallet, storage, chain, user, partnerships, thirdParty, communities, events, offices, navigation).
27
+ 2. Registers a `window.addEventListener('message', ...)` listener that routes `SDKResponse` messages from `window.parent`.
28
+ 3. Sends an `{ type: 'app:ready', payload: null }` postMessage to `window.parent` to notify the host that the app iframe is ready.
29
+
30
+ ### `destroy(): void`
31
+
32
+ Call when the app is being torn down. Removes the message event listener, calls `this.navigation.destroy()` to clean up deep-link listeners, and clears all pending request promises.
33
+
34
+ ### Internal: `request(type: string, payload?: any): Promise<any>`
35
+
36
+ Used by all access classes. Sends an `SDKRequest` via `window.parent.postMessage` and returns a promise that resolves/rejects when the host responds. Requests time out after **30 000 ms**.
37
+
38
+ ## PostMessage Protocol Types
39
+
40
+ ```typescript
41
+ interface SDKRequest {
42
+ type: string; // e.g. 'wallet:getBalance'
43
+ requestId: string; // `${Date.now()}-${incrementingId}`
44
+ payload?: any;
45
+ }
46
+
47
+ interface SDKResponse {
48
+ type: 'response' | 'error';
49
+ requestId: string;
50
+ result?: any;
51
+ error?: string;
52
+ }
53
+ ```
54
+
55
+ ## Module Getters
56
+
57
+ | Getter | Returns | Module |
58
+ |---|---|---|
59
+ | `sdk.getWallet()` | `WalletAccess` | Wallet |
60
+ | `sdk.getStorage()` | `StorageAccess` | Storage |
61
+ | `sdk.getChain()` | `ChainAccess` | Chain |
62
+ | `sdk.getUser()` | `UserAccess` | User |
63
+ | `sdk.getPartnerships()` | `PartnershipsAccess` | Partnerships |
64
+ | `sdk.getThirdParty()` | `ThirdPartyAccess` | Third-Party |
65
+ | `sdk.getCommunities()` | `CommunitiesAccess` | Communities |
66
+ | `sdk.getEvents()` | `EventsAccess` | Events |
67
+ | `sdk.getOffices()` | `OfficesAccess` | Offices |
68
+ | `sdk.getNavigation()` | `NavigationAccess` | Navigation |
69
+
70
+ ---
71
+
72
+ ## Security
73
+
74
+ ### Allowed Origins
75
+
76
+ The SDK defines three allowed Frontier Wallet origins. Apps should only accept messages from these:
77
+
78
+ | Environment | Origin |
79
+ |---|---|
80
+ | Development | `http://localhost:5173` |
81
+ | Sandbox | `https://sandbox.os.frontiertower.io` |
82
+ | Production | `https://os.frontiertower.io` |
83
+
84
+ ### Access Controls Verification
85
+
86
+ The `user:getVerifiedAccessControls` method provides a tamper-proof way to verify user access. The flow:
87
+
88
+ 1. The PWA host relays a `SignedAccessControls` envelope from the Frontier API server.
89
+ 2. The SDK decodes the Base64 payload, computes its SHA-256 hash, and verifies the ECDSA secp256k1 signature against a hardcoded public key for the current environment stage.
90
+ 3. If the signature is valid, the decoded `AccessControlsPayload` is returned.
91
+ 4. If invalid, the method throws -- the app should deny access.
92
+
93
+ Supported stages and their public keys (uncompressed secp256k1, hex):
94
+
95
+ | Stage(s) | Key |
96
+ |---|---|
97
+ | `test` | `04aab6c393...` (test-only key) |
98
+ | `development`, `local`, `sandbox`, `staging` | `04dc3ab0e1...` (shared dev/sandbox key) |
99
+ | `production` | `045d1a0f9c...` (production key) |
100
+
101
+ **Rule: Always use `getVerifiedAccessControls()` for access-gating decisions.** Do not trust unsigned user data from other SDK methods for gating features, content, or permissions.
102
+
103
+ ### PostMessage Security
104
+
105
+ - The SDK sends requests to `window.parent` with `'*'` as the target origin.
106
+ - The SDK only processes responses where `event.source === window.parent`.
107
+ - Requests auto-expire after 30 seconds.
108
+
109
+ ---
110
+
111
+ ## Wildcard Permissions
112
+
113
+ Each module supports a wildcard permission that grants access to all methods in that module:
114
+
115
+ | Wildcard | Grants |
116
+ |---|---|
117
+ | `wallet:*` | All wallet permissions |
118
+ | `storage:*` | All storage permissions |
119
+ | `chain:*` | All chain permissions |
120
+ | `user:*` | All user permissions |
121
+ | `partnerships:*` | All partnerships permissions |
122
+ | `thirdParty:*` | All third-party permissions |
123
+ | `communities:*` | All communities permissions |
124
+ | `events:*` | All events permissions |
125
+ | `offices:*` | All offices permissions |
126
+ | `navigation:*` | All navigation permissions |
@@ -0,0 +1,49 @@
1
+ # Navigation Module
2
+
3
+ **Trigger keywords:** navigate, deep link, deeplink, open app, app link, cross-app, redirect, launch app, inter-app
4
+
5
+ Access via `sdk.getNavigation()`. App-to-app deep linking. Allows apps to navigate to other Frontier OS apps and receive incoming deep link data.
6
+
7
+ ---
8
+
9
+ ## Methods
10
+
11
+ ```typescript
12
+ openApp(appId: string, options?: NavigationOpenAppOptions): Promise<void>
13
+ ```
14
+ Navigate the host to another app in the Frontier OS ecosystem. `appId` is the target app ID from the Frontier app registry. `options.path` provides an optional deep link path for the target app. `options.params` provides optional key-value params for the target app. Permission: `navigation:openApp`
15
+
16
+ ```typescript
17
+ close(): Promise<void>
18
+ ```
19
+ Close the current app and return to the previous screen. Permission: `navigation:close`
20
+
21
+ ```typescript
22
+ onDeepLink(callback: (data: DeepLinkData) => void): () => void
23
+ ```
24
+ Register a callback for incoming deep link data. Called when this app was opened via another app's `openApp()` call. Returns an unsubscribe function. No permission required (passive listener).
25
+
26
+ ---
27
+
28
+ ## Types
29
+
30
+ ```typescript
31
+ interface NavigationOpenAppOptions {
32
+ path?: string;
33
+ params?: Record<string, string>;
34
+ }
35
+
36
+ interface DeepLinkData {
37
+ path?: string;
38
+ params?: Record<string, string>;
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Permissions (2)
45
+
46
+ | Permission | Description |
47
+ |---|---|
48
+ | `navigation:openApp` | Navigate to another app |
49
+ | `navigation:close` | Close current app |
@@ -0,0 +1,76 @@
1
+ # Offices Module
2
+
3
+ **Trigger keywords:** office, access pass, building, door, entry, visitor, check-in, checkin, physical access, facility
4
+
5
+ Access via `sdk.getOffices()`. Manage office access passes for membership contracts.
6
+
7
+ All endpoints require authentication, an active subscription, and manager status on the membership contract's organization (or superuser).
8
+
9
+ ---
10
+
11
+ ## Methods
12
+
13
+ ```typescript
14
+ createAccessPass(payload: CreateAccessPassRequest): Promise<AccessPass>
15
+ ```
16
+ Create an access pass for a membership contract. Auto-creates an inactive account if the user does not exist. Each user can only have one active pass per contract. Permission: `offices:createAccessPass`
17
+
18
+ ```typescript
19
+ listAccessPasses(payload?: ListAccessPassesParams): Promise<PaginatedResponse<AccessPass>>
20
+ ```
21
+ List access passes for contracts the user manages. Active only by default; set `includeRevoked: true` for all. Ordered newest first. Permission: `offices:listAccessPasses`
22
+
23
+ ```typescript
24
+ getAccessPass(payload: { id: number }): Promise<AccessPass>
25
+ ```
26
+ Get an access pass by ID. Permission: `offices:getAccessPass`
27
+
28
+ ```typescript
29
+ revokeAccessPass(payload: { id: number }): Promise<void>
30
+ ```
31
+ Revoke an access pass. Cannot revoke an already-revoked pass. Permission: `offices:revokeAccessPass`
32
+
33
+ ---
34
+
35
+ ## Types
36
+
37
+ ```typescript
38
+ type AccessPassStatus = 'active' | 'revoked';
39
+
40
+ interface AccessPass {
41
+ id: number;
42
+ email: string;
43
+ firstName: string;
44
+ lastName: string;
45
+ status: AccessPassStatus;
46
+ membershipContract: number;
47
+ contractReference: string;
48
+ createdAt: string;
49
+ revokedAt: string | null;
50
+ updatedAt: string;
51
+ }
52
+
53
+ interface CreateAccessPassRequest {
54
+ email: string;
55
+ firstName: string;
56
+ lastName: string;
57
+ membershipContract: number;
58
+ }
59
+
60
+ interface ListAccessPassesParams {
61
+ limit?: number;
62
+ offset?: number;
63
+ includeRevoked?: boolean;
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Permissions (4)
70
+
71
+ | Permission | Description |
72
+ |---|---|
73
+ | `offices:createAccessPass` | Create an access pass for a membership contract |
74
+ | `offices:listAccessPasses` | List access passes for managed contracts (paginated) |
75
+ | `offices:getAccessPass` | Retrieve an access pass by ID |
76
+ | `offices:revokeAccessPass` | Revoke an access pass |
@@ -0,0 +1,111 @@
1
+ # Partnerships Module
2
+
3
+ **Trigger keywords:** sponsor, partnership, partner, sponsorship, benefactor, supporter, patron
4
+
5
+ Access via `sdk.getPartnerships()`. Manage sponsors and sponsor passes.
6
+
7
+ ---
8
+
9
+ ## Methods
10
+
11
+ ```typescript
12
+ createSponsorPass(payload: CreateSponsorPassRequest): Promise<SponsorPass>
13
+ ```
14
+ Create a new SponsorPass. Permission: `partnerships:createSponsorPass`
15
+
16
+ ```typescript
17
+ listActiveSponsorPasses(payload?: ListSponsorPassesParams): Promise<PaginatedResponse<SponsorPass>>
18
+ ```
19
+ List active (non-revoked) SponsorPasses, paginated. Permission: `partnerships:listActiveSponsorPasses`
20
+
21
+ ```typescript
22
+ listAllSponsorPasses(payload?: ListAllSponsorPassesParams): Promise<PaginatedResponse<SponsorPass>>
23
+ ```
24
+ List all SponsorPasses, optionally including revoked. Permission: `partnerships:listAllSponsorPasses`
25
+
26
+ ```typescript
27
+ listSponsors(payload?: ListSponsorsParams): Promise<PaginatedResponse<Sponsor>>
28
+ ```
29
+ List sponsors the user manages, paginated. Permission: `partnerships:listSponsors`
30
+
31
+ ```typescript
32
+ getSponsor(payload: { id: number }): Promise<Sponsor>
33
+ ```
34
+ Retrieve a Sponsor by ID. Permission: `partnerships:getSponsor`
35
+
36
+ ```typescript
37
+ getSponsorPass(payload: { id: number }): Promise<SponsorPass>
38
+ ```
39
+ Retrieve a SponsorPass by ID. Permission: `partnerships:getSponsorPass`
40
+
41
+ ```typescript
42
+ revokeSponsorPass(payload: { id: number }): Promise<void>
43
+ ```
44
+ Revoke (not delete) a SponsorPass. Permission: `partnerships:revokeSponsorPass`
45
+
46
+ ---
47
+
48
+ ## Types
49
+
50
+ ```typescript
51
+ type SponsorPassStatus = 'active' | 'revoked';
52
+
53
+ interface SponsorPass {
54
+ id: number;
55
+ sponsor: number;
56
+ sponsorName: string;
57
+ firstName: string;
58
+ lastName: string;
59
+ email: string;
60
+ status: SponsorPassStatus;
61
+ expiresAt: string | null;
62
+ createdAt: string;
63
+ updatedAt: string;
64
+ revokedAt: string | null;
65
+ }
66
+
67
+ interface CreateSponsorPassRequest {
68
+ sponsor: number;
69
+ firstName: string;
70
+ lastName: string;
71
+ email: string;
72
+ expiresAt?: string;
73
+ }
74
+
75
+ interface ListSponsorPassesParams {
76
+ limit?: number;
77
+ offset?: number;
78
+ }
79
+
80
+ interface ListAllSponsorPassesParams extends ListSponsorPassesParams {
81
+ includeRevoked?: boolean;
82
+ }
83
+
84
+ interface Sponsor {
85
+ id: number;
86
+ name: string;
87
+ dailyRate: string;
88
+ notes: string;
89
+ createdAt: string;
90
+ updatedAt: string;
91
+ }
92
+
93
+ interface ListSponsorsParams {
94
+ limit?: number;
95
+ offset?: number;
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Permissions (7)
102
+
103
+ | Permission | Description |
104
+ |---|---|
105
+ | `partnerships:listSponsors` | List sponsors you manage (paginated) |
106
+ | `partnerships:getSponsor` | Retrieve a Sponsor by ID |
107
+ | `partnerships:createSponsorPass` | Create a SponsorPass |
108
+ | `partnerships:listActiveSponsorPasses` | List active SponsorPasses (paginated) |
109
+ | `partnerships:listAllSponsorPasses` | List all SponsorPasses (paginated) |
110
+ | `partnerships:getSponsorPass` | Retrieve a SponsorPass by ID |
111
+ | `partnerships:revokeSponsorPass` | Revoke a SponsorPass |
@@ -0,0 +1,44 @@
1
+ # Storage Module
2
+
3
+ **Trigger keywords:** storage, persist, save, preferences, settings, cache, remember, state, draft, favorites, bookmarks
4
+
5
+ Access via `sdk.getStorage()`. Provides persistent key-value storage scoped to the app.
6
+
7
+ ---
8
+
9
+ ## Methods
10
+
11
+ ```typescript
12
+ get<T = any>(key: string): Promise<T>
13
+ ```
14
+ Read a value by key. Permission: `storage:get`
15
+
16
+ ```typescript
17
+ set(key: string, value: any): Promise<void>
18
+ ```
19
+ Write a value by key. Permission: `storage:set`
20
+
21
+ ```typescript
22
+ remove(key: string): Promise<void>
23
+ ```
24
+ Delete a key. Permission: `storage:remove`
25
+
26
+ ```typescript
27
+ clear(): Promise<void>
28
+ ```
29
+ Delete all keys. Permission: `storage:clear`
30
+
31
+ ## Types
32
+
33
+ No additional types -- uses generic `T` for get, `any` for set.
34
+
35
+ ---
36
+
37
+ ## Permissions (4)
38
+
39
+ | Permission | Description |
40
+ |---|---|
41
+ | `storage:get` | Read from persistent storage |
42
+ | `storage:set` | Write to persistent storage |
43
+ | `storage:remove` | Remove key from persistent storage |
44
+ | `storage:clear` | Clear all persistent storage |