@sprigr/apps-app-sdk 0.14.0 → 0.15.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.ts +263 -1
- package/dist/index.js +109 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2096,6 +2096,268 @@ declare function deriveConfirmationPolicy(registry: Iterable<string>, opts: Deri
|
|
|
2096
2096
|
*/
|
|
2097
2097
|
declare function commonDestructiveVerbs(subject: string): VerbRule[];
|
|
2098
2098
|
|
|
2099
|
+
/**
|
|
2100
|
+
* The inbox contract for mail and ticket apps (sprigr-team decision 0150).
|
|
2101
|
+
*
|
|
2102
|
+
* An app that writes into the Sprigr inbox does four things, all typed here:
|
|
2103
|
+
*
|
|
2104
|
+
* 1. Declares each channel it writes under `capabilities.inbox_channels` in
|
|
2105
|
+
* its manifest (`InboxChannelDeclaration`). Append refuses undeclared
|
|
2106
|
+
* channels, and replies cannot send without a declared send tool.
|
|
2107
|
+
* 2. Appends synced messages with `env.SPRIGR.inbox.append` (`SyncedMessage`,
|
|
2108
|
+
* `InboxOwner` on every batch). `withSprigrInboxFallback` makes that work
|
|
2109
|
+
* from an inline route too.
|
|
2110
|
+
* 3. Implements the declared send tool on the one `sprigr.inbox.send/v1`
|
|
2111
|
+
* argument shape, through `defineInboxSendHandler`, which enforces the
|
|
2112
|
+
* mailbox-owner rule so it cannot be forgotten.
|
|
2113
|
+
* 4. If it collects a credential on its own page (no OAuth bouncer),
|
|
2114
|
+
* reports the connection with `connectComplete`, which forwards the
|
|
2115
|
+
* platform-signed viewer token so the platform knows who connected.
|
|
2116
|
+
*/
|
|
2117
|
+
|
|
2118
|
+
declare const INBOX_SEND_CONTRACT_V1: "sprigr.inbox.send/v1";
|
|
2119
|
+
interface InboxSendSupports {
|
|
2120
|
+
bcc: boolean;
|
|
2121
|
+
html: boolean;
|
|
2122
|
+
recipients: boolean;
|
|
2123
|
+
importance: boolean;
|
|
2124
|
+
internal_note: boolean;
|
|
2125
|
+
}
|
|
2126
|
+
/** One entry of `capabilities.inbox_channels`. Validated at publish. */
|
|
2127
|
+
interface InboxChannelDeclaration {
|
|
2128
|
+
/** Must start with your slug in snake case plus `_` (`acme-mail` -> `acme_mail_...`). */
|
|
2129
|
+
channel: string;
|
|
2130
|
+
kind: 'email' | 'ticket';
|
|
2131
|
+
/** 1-40 printable characters, no `<` or `>`. */
|
|
2132
|
+
label: string;
|
|
2133
|
+
icon: 'mail' | 'ticket' | 'chat';
|
|
2134
|
+
send?: {
|
|
2135
|
+
tool: string;
|
|
2136
|
+
contract: typeof INBOX_SEND_CONTRACT_V1;
|
|
2137
|
+
returns_message_id: {
|
|
2138
|
+
send: boolean;
|
|
2139
|
+
reply: boolean;
|
|
2140
|
+
};
|
|
2141
|
+
attachments: 'file_key' | 'inline' | 'none';
|
|
2142
|
+
supports: InboxSendSupports;
|
|
2143
|
+
};
|
|
2144
|
+
signature_family?: string;
|
|
2145
|
+
attachment_repair_tool?: string;
|
|
2146
|
+
/** https only, host in `permissions.network_domains`; placeholders
|
|
2147
|
+
* `{source_thread_id}` and `{source_id}`. */
|
|
2148
|
+
deep_link_template?: string | null;
|
|
2149
|
+
}
|
|
2150
|
+
interface SyncedAttachment {
|
|
2151
|
+
filename: string;
|
|
2152
|
+
mimeType: string;
|
|
2153
|
+
size: number;
|
|
2154
|
+
/** Opaque to the platform; your `fetch_attachment` tool resolves it. It
|
|
2155
|
+
* must identify the connection, because that dispatch carries no actor. */
|
|
2156
|
+
providerAttachmentId?: string;
|
|
2157
|
+
contentId?: string;
|
|
2158
|
+
inline?: boolean;
|
|
2159
|
+
}
|
|
2160
|
+
interface SyncedMessage {
|
|
2161
|
+
/** Unique within the channel; the platform dedups on (channel, sourceId). */
|
|
2162
|
+
sourceId: string;
|
|
2163
|
+
sourceIndex: string;
|
|
2164
|
+
sourceThreadId?: string;
|
|
2165
|
+
/** RFC 5322 Message-ID (brackets optional). */
|
|
2166
|
+
rfcMessageId?: string;
|
|
2167
|
+
channel?: string;
|
|
2168
|
+
direction: 'inbound' | 'outbound' | 'internal';
|
|
2169
|
+
fromName?: string;
|
|
2170
|
+
fromEmail?: string;
|
|
2171
|
+
toRecipients?: Array<{
|
|
2172
|
+
name: string;
|
|
2173
|
+
email: string;
|
|
2174
|
+
}>;
|
|
2175
|
+
ccRecipients?: Array<{
|
|
2176
|
+
name: string;
|
|
2177
|
+
email: string;
|
|
2178
|
+
}>;
|
|
2179
|
+
subject?: string;
|
|
2180
|
+
/** Plain text. May be '' when `bodyHtml` carries the content. */
|
|
2181
|
+
body: string;
|
|
2182
|
+
bodyHtml?: string;
|
|
2183
|
+
/** ISO 8601. */
|
|
2184
|
+
timestamp: string;
|
|
2185
|
+
sourceIsRead?: boolean;
|
|
2186
|
+
tags?: string[];
|
|
2187
|
+
attachments?: SyncedAttachment[];
|
|
2188
|
+
}
|
|
2189
|
+
interface InboxAppendRequest {
|
|
2190
|
+
channel?: string;
|
|
2191
|
+
/** At most 500 per call. */
|
|
2192
|
+
messages: SyncedMessage[];
|
|
2193
|
+
/** The MAILBOX owner (from your connection row), never the dispatch caller. */
|
|
2194
|
+
owner?: InboxOwner;
|
|
2195
|
+
}
|
|
2196
|
+
interface InboxAppendResult {
|
|
2197
|
+
ok: boolean;
|
|
2198
|
+
threadsCreated: number;
|
|
2199
|
+
messagesLinked: number;
|
|
2200
|
+
threadIds: string[];
|
|
2201
|
+
contentlessSkipped?: number;
|
|
2202
|
+
}
|
|
2203
|
+
interface InboxFoldersRequest {
|
|
2204
|
+
channel?: string;
|
|
2205
|
+
/** At most 200 per call. Folder names match manual Sprigr folders. */
|
|
2206
|
+
items: Array<{
|
|
2207
|
+
sourceId: string;
|
|
2208
|
+
addFolders?: string[];
|
|
2209
|
+
removeFolders?: string[];
|
|
2210
|
+
}>;
|
|
2211
|
+
owner?: InboxOwner;
|
|
2212
|
+
}
|
|
2213
|
+
interface InboxFoldersResult {
|
|
2214
|
+
ok: boolean;
|
|
2215
|
+
filed: number;
|
|
2216
|
+
unfiled: number;
|
|
2217
|
+
skippedGuarded: number;
|
|
2218
|
+
unknownFolders: string[];
|
|
2219
|
+
}
|
|
2220
|
+
interface SprigrInboxApi {
|
|
2221
|
+
append(req: InboxAppendRequest): Promise<InboxAppendResult>;
|
|
2222
|
+
folders?(req: InboxFoldersRequest): Promise<InboxFoldersResult>;
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Return an env whose `SPRIGR.inbox` works from any execution context.
|
|
2226
|
+
*
|
|
2227
|
+
* `env.SPRIGR` is injected only on the `/__sprigr/*` dispatch path. An inline
|
|
2228
|
+
* route (a provider push receiver, your settings page) gets the raw bindings,
|
|
2229
|
+
* so this wires the same contract over the install-token HTTP bridge. Append
|
|
2230
|
+
* and folders THROW on a non-2xx, so a caller never advances a sync cursor
|
|
2231
|
+
* past messages the platform did not accept. With no bridge bindings the env
|
|
2232
|
+
* comes back unchanged and `SPRIGR.inbox` stays undefined: fail closed.
|
|
2233
|
+
*/
|
|
2234
|
+
declare function withSprigrInboxFallback<E extends WfpBridgeEnv & object>(env: E): E;
|
|
2235
|
+
interface InboxAddress {
|
|
2236
|
+
email: string;
|
|
2237
|
+
name?: string;
|
|
2238
|
+
}
|
|
2239
|
+
/** What the platform sends your declared send tool. snake_case. */
|
|
2240
|
+
interface InboxSendV1Args {
|
|
2241
|
+
contract: typeof INBOX_SEND_CONTRACT_V1;
|
|
2242
|
+
from_email?: string;
|
|
2243
|
+
to?: InboxAddress[];
|
|
2244
|
+
cc?: InboxAddress[];
|
|
2245
|
+
bcc?: InboxAddress[];
|
|
2246
|
+
subject?: string;
|
|
2247
|
+
body_text: string;
|
|
2248
|
+
body_html?: string;
|
|
2249
|
+
attachments?: Array<{
|
|
2250
|
+
file_key: string;
|
|
2251
|
+
filename: string;
|
|
2252
|
+
content_type: string;
|
|
2253
|
+
}> | Array<{
|
|
2254
|
+
filename: string;
|
|
2255
|
+
mime_type?: string;
|
|
2256
|
+
base64_body: string;
|
|
2257
|
+
}>;
|
|
2258
|
+
/** `source_id` / `source_thread_id` are YOUR stored ids, present only when
|
|
2259
|
+
* your install received the mail being answered. */
|
|
2260
|
+
reply?: {
|
|
2261
|
+
source_id?: string;
|
|
2262
|
+
source_thread_id?: string;
|
|
2263
|
+
rfc_message_id?: string;
|
|
2264
|
+
references: string[];
|
|
2265
|
+
};
|
|
2266
|
+
internal_note?: boolean;
|
|
2267
|
+
author_email?: string;
|
|
2268
|
+
importance?: 'high' | 'low';
|
|
2269
|
+
idempotency_key?: string;
|
|
2270
|
+
}
|
|
2271
|
+
/** What your send tool returns. `message_id` must equal the `sourceId` your
|
|
2272
|
+
* sync will write for the sent copy, so the Sent echo dedups. */
|
|
2273
|
+
interface InboxSendV1Result {
|
|
2274
|
+
message_id?: string;
|
|
2275
|
+
internet_message_id?: string;
|
|
2276
|
+
thread_id?: string;
|
|
2277
|
+
}
|
|
2278
|
+
/** Your connection record, as far as the owner check needs it. */
|
|
2279
|
+
interface InboxSendConnection {
|
|
2280
|
+
/** `u:<platformUserId>` or `a:<agentId>`: the key the connection is filed under. */
|
|
2281
|
+
actorKey: string;
|
|
2282
|
+
}
|
|
2283
|
+
interface InboxSendHandlerImpl<E, C extends InboxSendConnection> {
|
|
2284
|
+
/** The connection that owns one of YOUR stored source ids, or null when the
|
|
2285
|
+
* id is not yours. Parse your own id format here. */
|
|
2286
|
+
connectionForSourceId(env: E, sourceId: string): Promise<C | null>;
|
|
2287
|
+
/** The connection this actor key sends a NEW mail from. When `fromEmail` is
|
|
2288
|
+
* set, return only a connection that may send as that address, else null. */
|
|
2289
|
+
connectionForActor(env: E, actorKey: string, fromEmail: string | undefined): Promise<C | null>;
|
|
2290
|
+
send(env: E, connection: C, args: InboxSendV1Args): Promise<InboxSendV1Result>;
|
|
2291
|
+
}
|
|
2292
|
+
declare class InboxSendRefusal extends Error {
|
|
2293
|
+
readonly code: 'no_caller_identity' | 'not_your_mailbox' | 'not_connected' | 'invalid_args';
|
|
2294
|
+
constructor(code: 'no_caller_identity' | 'not_your_mailbox' | 'not_connected' | 'invalid_args', message: string);
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Build the handler for your declared `sprigr.inbox.send/v1` tool.
|
|
2298
|
+
*
|
|
2299
|
+
* THE MAILBOX-OWNER RULE, enforced here so no app can omit it: replies are
|
|
2300
|
+
* dispatched under the WRITER's actor, and anyone who can read a thread can
|
|
2301
|
+
* press Reply. So the connection you send from must be filed under one of the
|
|
2302
|
+
* writer's own keys (`u:<platformUserId>`, `a:<agentId>`). A reply whose
|
|
2303
|
+
* source belongs to someone else's connection is REFUSED, never re-routed to
|
|
2304
|
+
* the writer's own mailbox and never sent from the owner's: either would
|
|
2305
|
+
* deliver mail the writer is not entitled to send.
|
|
2306
|
+
*/
|
|
2307
|
+
declare function defineInboxSendHandler<E, C extends InboxSendConnection>(impl: InboxSendHandlerImpl<E, C>): (args: Record<string, unknown>, env: E) => Promise<ToolResult<InboxSendV1Result>>;
|
|
2308
|
+
interface ConnectCompleteRequest {
|
|
2309
|
+
/** The raw `X-Sprigr-Viewer` header of the PAGE request that submitted the
|
|
2310
|
+
* credential. The platform takes the user only from this. */
|
|
2311
|
+
viewer_token: string;
|
|
2312
|
+
bind?: {
|
|
2313
|
+
kind: 'user';
|
|
2314
|
+
} | {
|
|
2315
|
+
kind: 'agent';
|
|
2316
|
+
agent_id: string;
|
|
2317
|
+
};
|
|
2318
|
+
account: {
|
|
2319
|
+
email: string;
|
|
2320
|
+
name?: string;
|
|
2321
|
+
};
|
|
2322
|
+
identities?: Array<{
|
|
2323
|
+
email: string;
|
|
2324
|
+
name?: string;
|
|
2325
|
+
}>;
|
|
2326
|
+
}
|
|
2327
|
+
interface ConnectCompleteResult {
|
|
2328
|
+
ok: boolean;
|
|
2329
|
+
/** File your connection under this key. */
|
|
2330
|
+
actor_key: string;
|
|
2331
|
+
identities: {
|
|
2332
|
+
inserted: number;
|
|
2333
|
+
updated: number;
|
|
2334
|
+
deleted: number;
|
|
2335
|
+
};
|
|
2336
|
+
/** The same address already connected through another app in the company. */
|
|
2337
|
+
existing_mailboxes: Array<{
|
|
2338
|
+
app_slug: string;
|
|
2339
|
+
email: string;
|
|
2340
|
+
account_email: string;
|
|
2341
|
+
}>;
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Report a mailbox connected on your own page (token paste, app password).
|
|
2345
|
+
* Call it from the inline route that handled the page's POST, passing that
|
|
2346
|
+
* request's `X-Sprigr-Viewer` header. Throws on a non-2xx with `.status` and
|
|
2347
|
+
* `.error` (`viewer_unverified`, `viewer_not_a_member`, `agent_bind_refused`,
|
|
2348
|
+
* `agent_bind_requires_admin`, ...).
|
|
2349
|
+
*/
|
|
2350
|
+
declare function connectComplete(env: WfpBridgeEnv, req: ConnectCompleteRequest): Promise<ConnectCompleteResult>;
|
|
2351
|
+
/** Retire a connection's From-picker identities (install token only, so a
|
|
2352
|
+
* background sync can call it when a credential dies). */
|
|
2353
|
+
declare function connectDisconnect(env: WfpBridgeEnv, req: {
|
|
2354
|
+
actor_key: string;
|
|
2355
|
+
account_email?: string;
|
|
2356
|
+
}): Promise<{
|
|
2357
|
+
ok: boolean;
|
|
2358
|
+
deleted: number;
|
|
2359
|
+
}>;
|
|
2360
|
+
|
|
2099
2361
|
/**
|
|
2100
2362
|
* @sprigr/apps-app-sdk
|
|
2101
2363
|
*
|
|
@@ -2636,4 +2898,4 @@ interface ExecutionContextLike {
|
|
|
2636
2898
|
}
|
|
2637
2899
|
type HandlerFn<TArgs, TResult> = (args: TArgs, env: Record<string, unknown>, ctx?: ExecutionContextLike) => Promise<TResult>;
|
|
2638
2900
|
|
|
2639
|
-
export { APPROVAL_GRANTED_KEY, type Actor, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalGateOptions, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CaptureJournal, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type D1Like, type D1PreparedStatementLike, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, type InboxOwner, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type RequireApprovalOptions, type ResolveViewerOptions, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, type SprigrDataIndexOpts, type SprigrDataPartialUpdateBody, type SprigrDataPartialUpdateOpts, type SprigrDataPartialUpdateResult, type SprigrDataPatch, type SprigrDataSearchOpts, type SprigrDataSearchResult, SprigrDataValidationError, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type SprigrFilesListItem, type SprigrFilesListResult, type SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, type ToolArgs, type ToolHandler, type ToolResult, type ToolWrapperOptions, UNIT_SEP, type UndoApplyOptions, type UndoApplyResult, type UndoCaptureSpec, type UndoFidelity, VIEWER_ANONYMOUS, VIEWER_CONTEXT_HEADER, VIEWER_PLATFORM_USER_ID_HEADER, VIEWER_ROLE_HEADER, VIEWER_USER_ID_HEADER, type VerbRule, type ViewerContext, type ViewerContextFailure, type ViewerContextVerification, type ViewerEnv, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorTool, appFileUrl, applyConfirmationPolicies, approvalHash, archiveOfferRefusal, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, deleteAppFile, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emitMarketplaceEvent, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fullWarning, getAppFile, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, manifestIsFresh, offerUndo, overlaySprigr, ownerFromActorKey, parseActor, partialUpdateData, putAppFile, putAppFileStream, randomHex, recreatedWarning, refuseWithoutForce, requireApproval, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrLogFallback };
|
|
2901
|
+
export { APPROVAL_GRANTED_KEY, type Actor, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalGateOptions, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CaptureJournal, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type ConnectCompleteRequest, type ConnectCompleteResult, type D1Like, type D1PreparedStatementLike, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, INBOX_SEND_CONTRACT_V1, type InboxAddress, type InboxAppendRequest, type InboxAppendResult, type InboxChannelDeclaration, type InboxFoldersRequest, type InboxFoldersResult, type InboxOwner, type InboxSendConnection, type InboxSendHandlerImpl, InboxSendRefusal, type InboxSendSupports, type InboxSendV1Args, type InboxSendV1Result, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type RequireApprovalOptions, type ResolveViewerOptions, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, type SprigrDataIndexOpts, type SprigrDataPartialUpdateBody, type SprigrDataPartialUpdateOpts, type SprigrDataPartialUpdateResult, type SprigrDataPatch, type SprigrDataSearchOpts, type SprigrDataSearchResult, SprigrDataValidationError, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type SprigrFilesListItem, type SprigrFilesListResult, type SprigrInboxApi, type SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, type SyncedAttachment, type SyncedMessage, type ToolArgs, type ToolHandler, type ToolResult, type ToolWrapperOptions, UNIT_SEP, type UndoApplyOptions, type UndoApplyResult, type UndoCaptureSpec, type UndoFidelity, VIEWER_ANONYMOUS, VIEWER_CONTEXT_HEADER, VIEWER_PLATFORM_USER_ID_HEADER, VIEWER_ROLE_HEADER, VIEWER_USER_ID_HEADER, type VerbRule, type ViewerContext, type ViewerContextFailure, type ViewerContextVerification, type ViewerEnv, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorTool, appFileUrl, applyConfirmationPolicies, approvalHash, archiveOfferRefusal, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, connectComplete, connectDisconnect, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, defineInboxSendHandler, deleteAppFile, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emitMarketplaceEvent, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fullWarning, getAppFile, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, manifestIsFresh, offerUndo, overlaySprigr, ownerFromActorKey, parseActor, partialUpdateData, putAppFile, putAppFileStream, randomHex, recreatedWarning, refuseWithoutForce, requireApproval, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrInboxFallback, withSprigrLogFallback };
|
package/dist/index.js
CHANGED
|
@@ -1488,6 +1488,109 @@ function commonDestructiveVerbs(subject) {
|
|
|
1488
1488
|
{ match: "deactivate_", tier: "always", describe: (_n, rest) => `Deactivate ${rest}${where}` }
|
|
1489
1489
|
];
|
|
1490
1490
|
}
|
|
1491
|
+
|
|
1492
|
+
// src/inbox.ts
|
|
1493
|
+
var INBOX_SEND_CONTRACT_V1 = "sprigr.inbox.send/v1";
|
|
1494
|
+
function withSprigrInboxFallback(env) {
|
|
1495
|
+
const bridge = resolveInstallBridge(env);
|
|
1496
|
+
const existing = env.SPRIGR ?? void 0;
|
|
1497
|
+
const folders = bridge ? async (req) => await installTokenPost(bridge, "/internal/wfp/inbox/folders", req, { label: "inbox folders" }) : void 0;
|
|
1498
|
+
if (existing?.inbox) {
|
|
1499
|
+
if (existing.inbox.folders || !folders) return env;
|
|
1500
|
+
const inbox = existing.inbox;
|
|
1501
|
+
return overlaySprigr(env, { ...existing, inbox: { append: inbox.append.bind(inbox), folders } });
|
|
1502
|
+
}
|
|
1503
|
+
if (!bridge) return env;
|
|
1504
|
+
const append = async (req) => {
|
|
1505
|
+
const r = await installTokenPost(bridge, "/internal/wfp/inbox/append", req, { label: "inbox append" });
|
|
1506
|
+
return {
|
|
1507
|
+
ok: r.ok !== false,
|
|
1508
|
+
threadsCreated: r.threadsCreated ?? 0,
|
|
1509
|
+
messagesLinked: r.messagesLinked ?? 0,
|
|
1510
|
+
threadIds: Array.isArray(r.threadIds) ? r.threadIds : [],
|
|
1511
|
+
...typeof r.contentlessSkipped === "number" ? { contentlessSkipped: r.contentlessSkipped } : {}
|
|
1512
|
+
};
|
|
1513
|
+
};
|
|
1514
|
+
return overlaySprigr(env, { ...existing ?? {}, inbox: { append, folders } });
|
|
1515
|
+
}
|
|
1516
|
+
var InboxSendRefusal = class extends Error {
|
|
1517
|
+
constructor(code, message) {
|
|
1518
|
+
super(message);
|
|
1519
|
+
this.code = code;
|
|
1520
|
+
this.name = "InboxSendRefusal";
|
|
1521
|
+
}
|
|
1522
|
+
code;
|
|
1523
|
+
};
|
|
1524
|
+
function defineInboxSendHandler(impl) {
|
|
1525
|
+
return async (rawArgs, env) => {
|
|
1526
|
+
try {
|
|
1527
|
+
const actor = parseActor(rawArgs);
|
|
1528
|
+
const keys = [
|
|
1529
|
+
...actor?.platformUserId ? [`u:${actor.platformUserId}`] : [],
|
|
1530
|
+
...actor?.agentId ? [`a:${actor.agentId}`] : []
|
|
1531
|
+
];
|
|
1532
|
+
if (keys.length === 0) {
|
|
1533
|
+
throw new InboxSendRefusal("no_caller_identity", "The platform stamped no sender identity on this send.");
|
|
1534
|
+
}
|
|
1535
|
+
const args = rawArgs;
|
|
1536
|
+
if (args.contract !== INBOX_SEND_CONTRACT_V1 || typeof args.body_text !== "string") {
|
|
1537
|
+
throw new InboxSendRefusal("invalid_args", `Expected ${INBOX_SEND_CONTRACT_V1} arguments.`);
|
|
1538
|
+
}
|
|
1539
|
+
let connection = null;
|
|
1540
|
+
const sourceId = args.reply?.source_id;
|
|
1541
|
+
if (sourceId) {
|
|
1542
|
+
const owner = await impl.connectionForSourceId(env, sourceId);
|
|
1543
|
+
if (owner) {
|
|
1544
|
+
if (!keys.includes(owner.actorKey)) {
|
|
1545
|
+
throw new InboxSendRefusal(
|
|
1546
|
+
"not_your_mailbox",
|
|
1547
|
+
"This conversation arrived in a mailbox that belongs to someone else, so you cannot reply from it."
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
if (args.from_email) {
|
|
1551
|
+
const asFrom = await impl.connectionForActor(env, owner.actorKey, args.from_email);
|
|
1552
|
+
if (!asFrom || asFrom.actorKey !== owner.actorKey) {
|
|
1553
|
+
throw new InboxSendRefusal("not_your_mailbox", `This mailbox cannot send as ${args.from_email}.`);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
connection = owner;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
if (!connection) {
|
|
1560
|
+
for (const key of keys) {
|
|
1561
|
+
connection = await impl.connectionForActor(env, key, args.from_email);
|
|
1562
|
+
if (connection) break;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (!connection) {
|
|
1566
|
+
throw new InboxSendRefusal(
|
|
1567
|
+
"not_connected",
|
|
1568
|
+
args.from_email ? `You have no connection that can send as ${args.from_email}.` : "You have not connected a mailbox with this app."
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
if (!keys.includes(connection.actorKey)) {
|
|
1572
|
+
throw new InboxSendRefusal("not_your_mailbox", "Resolved connection does not belong to the sender.");
|
|
1573
|
+
}
|
|
1574
|
+
return { ok: true, result: await impl.send(env, connection, args) };
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
if (err instanceof InboxSendRefusal || err instanceof Error && err.name === "InboxSendRefusal") {
|
|
1577
|
+
const code = err.code;
|
|
1578
|
+
return { ok: false, error: `${code}: ${err.message}`, status: code === "invalid_args" ? 400 : 412 };
|
|
1579
|
+
}
|
|
1580
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
async function connectComplete(env, req) {
|
|
1585
|
+
const bridge = resolveInstallBridge(env);
|
|
1586
|
+
if (!bridge) throw new Error("connectComplete: SPRIGR_PLATFORM_BASE / SPRIGR_INSTALL_TOKEN are not bound");
|
|
1587
|
+
return await installTokenPost(bridge, "/internal/wfp/connect/complete", req, { label: "connect complete" });
|
|
1588
|
+
}
|
|
1589
|
+
async function connectDisconnect(env, req) {
|
|
1590
|
+
const bridge = resolveInstallBridge(env);
|
|
1591
|
+
if (!bridge) throw new Error("connectDisconnect: SPRIGR_PLATFORM_BASE / SPRIGR_INSTALL_TOKEN are not bound");
|
|
1592
|
+
return await installTokenPost(bridge, "/internal/wfp/connect/disconnect", req, { label: "connect disconnect" });
|
|
1593
|
+
}
|
|
1491
1594
|
export {
|
|
1492
1595
|
APPROVAL_GRANTED_KEY,
|
|
1493
1596
|
DATA_PARTIAL_UPDATE_PATH,
|
|
@@ -1496,6 +1599,8 @@ export {
|
|
|
1496
1599
|
DEFAULT_MAX_FILE_BYTES,
|
|
1497
1600
|
DEFAULT_MONEY_FIELDS,
|
|
1498
1601
|
DEFAULT_WRITE_PREFIXES,
|
|
1602
|
+
INBOX_SEND_CONTRACT_V1,
|
|
1603
|
+
InboxSendRefusal,
|
|
1499
1604
|
InvalidUndoEnvelopeError,
|
|
1500
1605
|
NotConnectedError,
|
|
1501
1606
|
SPRIGR_DATA_MAX_OBJECTS_PER_CALL,
|
|
@@ -1525,10 +1630,13 @@ export {
|
|
|
1525
1630
|
canPartialUpdate,
|
|
1526
1631
|
checkConfirmationPolicy,
|
|
1527
1632
|
commonDestructiveVerbs,
|
|
1633
|
+
connectComplete,
|
|
1634
|
+
connectDisconnect,
|
|
1528
1635
|
constantTimeEqual,
|
|
1529
1636
|
createMarketplaceEmitter,
|
|
1530
1637
|
createToolWrappers,
|
|
1531
1638
|
decodeState,
|
|
1639
|
+
defineInboxSendHandler,
|
|
1532
1640
|
deleteAppFile,
|
|
1533
1641
|
deriveConfirmationPolicy,
|
|
1534
1642
|
describeMissingBridge,
|
|
@@ -1575,5 +1683,6 @@ export {
|
|
|
1575
1683
|
validateLogEntry,
|
|
1576
1684
|
verifyViewerToken,
|
|
1577
1685
|
withSprigrEmitFallback,
|
|
1686
|
+
withSprigrInboxFallback,
|
|
1578
1687
|
withSprigrLogFallback
|
|
1579
1688
|
};
|