@opencxh/domain 1.83.0 → 1.85.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/entities/activity/types.d.ts +2 -0
- package/dist/entities/ai-profile/types.d.ts +7 -0
- package/dist/entities/contact/provider.d.ts +9 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -9
- package/dist/platform/communication.d.ts +50 -0
- package/dist/platform/presence.d.ts +25 -0
- package/dist/platform/storage.d.ts +49 -10
- package/package.json +1 -1
|
@@ -161,6 +161,8 @@ export type FileUploadedPayload = {
|
|
|
161
161
|
fileUrl: string;
|
|
162
162
|
fileType: string;
|
|
163
163
|
size: number;
|
|
164
|
+
/** Storage FilePointer id, when the file lives in the storage app (download via storage.file.{id}.download). */
|
|
165
|
+
fileId?: string;
|
|
164
166
|
};
|
|
165
167
|
export type InteractionStatusChangedPayload = {
|
|
166
168
|
fromStatus: "open" | "pending" | "closed";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OwnerScope } from '../contact/types';
|
|
1
2
|
export interface PredefinedPrompt {
|
|
2
3
|
name: string;
|
|
3
4
|
prompt: string;
|
|
@@ -5,6 +6,12 @@ export interface PredefinedPrompt {
|
|
|
5
6
|
export interface AIProfile<T extends Record<string, any> = Record<string, any>> {
|
|
6
7
|
id: string;
|
|
7
8
|
organizationId: string;
|
|
9
|
+
/**
|
|
10
|
+
* Who may use this profile. Company-wide (`org`) or bound to a `team`; profiles
|
|
11
|
+
* are never personal. Optional only during the migration window - the server
|
|
12
|
+
* defaults new profiles to `org` and backfills existing ones.
|
|
13
|
+
*/
|
|
14
|
+
ownerScope?: OwnerScope;
|
|
8
15
|
name: string;
|
|
9
16
|
description?: string;
|
|
10
17
|
accountId: string;
|
|
@@ -4,6 +4,15 @@ export interface ContactSearchQuery {
|
|
|
4
4
|
text?: string;
|
|
5
5
|
scope?: OwnerScope[];
|
|
6
6
|
limit?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Channels whose identities the search should cover. The composer passes the
|
|
9
|
+
* one acting channel; the intent starter passes every channel the user can
|
|
10
|
+
* act as. Providers search each identity they own among these and merge —
|
|
11
|
+
* e.g. Microsoft searches the relevant people of every shared mailbox in the
|
|
12
|
+
* list. Order matters: providers may cap the fan-out, so pass most-relevant
|
|
13
|
+
* first.
|
|
14
|
+
*/
|
|
15
|
+
channelIds?: string[];
|
|
7
16
|
}
|
|
8
17
|
export interface ContactProvider {
|
|
9
18
|
id: string;
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function i(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/\s+/g," ").trim()}function A(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function i(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/\s+/g," ").trim()}function A(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function o(e){if(!e||e<0)return"";const t=Math.floor(e/60),n=Math.floor(e%60);return`${t}:${n.toString().padStart(2,"0")}`}function I(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||i(t.body??"");return t.subject?`${t.subject} — ${n}`:n}case"CHAT_MESSAGE_SENT":case"CHAT_MESSAGE_RECEIVED":return e.payload.text??"";case"CHAT_RENAMED":return`Hernoemd naar "${e.payload.newName}"`;case"CHAT_MEMBER_JOINED":return`${e.payload.members.map(t=>t.name).join(", ")} toegevoegd`;case"CHAT_MEMBER_LEFT":return`${e.payload.members.map(t=>t.name).join(", ")} verlaten`;case"CHAT_CALL_STARTED":return"Gesprek gestart";case"CHAT_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"CHAT_EVENT":return e.payload.text??e.payload.eventType;case"VOICE_CALL_STARTED":case"VIDEO_CALL_STARTED":return"Gesprek gestart";case"VOICE_CALL_ANSWERED":case"VIDEO_CALL_ANSWERED":return"Gesprek aangenomen";case"VOICE_CALL_HOLD":case"VIDEO_CALL_HOLD":return"In de wacht";case"VOICE_CALL_UNHOLD":case"VIDEO_CALL_UNHOLD":return"Hervat";case"VOICE_CALL_ENDED":case"VIDEO_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"VOICE_CALL_MISSED":case"VIDEO_CALL_MISSED":return"Gemiste oproep";case"VOICE_CALL_FAILED":case"VIDEO_CALL_FAILED":return"Gesprek mislukt";case"VOICE_CALL_VOICEMAIL":return e.payload.transcription?.trim()?e.payload.transcription:"Voicemail ontvangen";case"TRANSCRIPT_ADDED":return(e.payload.segments??[]).map(t=>t.text).join(" ");case"AI_MESSAGE_ADDED":return e.payload.output?.text??e.payload.input?.text??"";case"MEETING_SCHEDULED":return`Vergadering gepland: ${e.payload.title}`;case"MEETING_STARTED":return`Vergadering gestart: ${e.payload.title}`;case"MEETING_ENDED":return`Vergadering beëindigd${e.payload.duration?` (${o(e.payload.duration)})`:""}`;case"MEETING_PARTICIPANT_JOINED":return`${e.payload.name} deelgenomen`;case"MEETING_PARTICIPANT_LEFT":return`${e.payload.name} verlaten`;case"COMMENT_ADDED":return e.payload.text??"";case"FILE_UPLOADED":return`Bestand: ${e.payload.fileName}`;case"INTERACTION_CREATED":return"Interactie aangemaakt";case"INTERACTION_STATUS_CHANGED":return`Status: ${e.payload.fromStatus} → ${e.payload.toStatus}`;case"INTERACTION_ASSIGNED":return"Interactie toegewezen";default:return""}}function _(e){return{activityId:e.id,type:e.type,snippet:A(I(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function T(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function g(e){return e.endpoints??[]}const D=e=>!!e.assignedUserId||!!e.assignedInboxId,S=e=>e.status==="closed",f=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,N=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function R(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}function u(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(a=>!n.has(a.intent)).map(a=>O(a,r[a.intent]));if(!e.extraIntents||e.extraIntents.length===0)return s;const E=new Set(s.map(a=>a.intent));for(const a of e.extraIntents)E.has(a.intent)||(s.push(a),E.add(a.intent));return s}function c(e,t){const n={};for(const r of e.intents){if(!r.togglable)continue;const s=t?.[r.intent];n[r.intent]=s??r.defaultEnabled??!0}return n}function L(e,t){const n=c(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function O(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function C(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const E of u(r,s))n.push({channel:r,description:s,capability:E})}return n}function M(e,t){return t.filter(n=>n.capability.intent===e)}function d(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function b(e,t){return d(e.scheme,t)}function P(e,t,n){const r=[];for(const s of e)for(const E of n)E.capability.intent===t&&E.capability.targetSchemes.includes(s.scheme)&&r.push({channelIntent:E,endpoint:s});return r}var p=(e=>(e.MAILTO="mailto",e.SIP="sip",e.TEL="tel",e.WEBHOOK="webhook",e.USERNAME="username",e.ID="id",e.CUSTOM="custom",e.URL="url",e.TELEGRAM="telegram",e.WHATSAPP="whatsapp",e.MESSENGER="messenger",e.INSTAGRAM="instagram",e.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e.CALENDAR="calendar",e))(p||{});const G="message_window",U="message_templates",V={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},m=(e,t)=>({intent:e,...t}),y="folder_management",H="remote_search",h={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},$=9e4,F="installation-id";exports.CommunicationScheme=p;exports.FEATURE_FOLDER_MANAGEMENT=y;exports.FEATURE_REMOTE_SEARCH=H;exports.INSTALLATION_HEADER=F;exports.InteractionParticipantRole=V;exports.PRESENCE_ONLINE_WINDOW_MS=$;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=U;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=G;exports.UNSUPPORTED=h;exports.buildActivityPreview=_;exports.buildChannelIntents=C;exports.defineIntent=m;exports.disabledIntentsFromCapabilities=L;exports.filterByEndpoint=b;exports.filterByIntent=M;exports.filterByTargetScheme=d;exports.getContactEndpoints=g;exports.getShortTitle=N;exports.getUrgencyScore=f;exports.isAssigned=D;exports.isClosed=S;exports.isInteractionUnseen=R;exports.matchContactToIntents=P;exports.resolveAccountCapabilities=c;exports.resolveChannelIntents=u;exports.resolveSignature=T;exports.stripHtml=i;
|
package/dist/index.d.ts
CHANGED
|
@@ -44,5 +44,6 @@ export * from './platform/ui';
|
|
|
44
44
|
export * from './platform/media';
|
|
45
45
|
export * from './platform/provider';
|
|
46
46
|
export * from './platform/push';
|
|
47
|
+
export * from './platform/presence';
|
|
47
48
|
export * from './platform/installation';
|
|
48
49
|
export * from './platform/storage';
|
package/dist/index.js
CHANGED
|
@@ -100,7 +100,7 @@ function T(e, t) {
|
|
|
100
100
|
function g(e) {
|
|
101
101
|
return e.endpoints ?? [];
|
|
102
102
|
}
|
|
103
|
-
const
|
|
103
|
+
const D = (e) => !!e.assignedUserId || !!e.assignedInboxId, f = (e) => e.status === "closed", L = (e) => ({
|
|
104
104
|
urgent: 10,
|
|
105
105
|
high: 5,
|
|
106
106
|
normal: 2,
|
|
@@ -137,7 +137,7 @@ function A(e, t) {
|
|
|
137
137
|
transport: t.transport ?? e.transport
|
|
138
138
|
} : e;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function O(e, t) {
|
|
141
141
|
const n = [];
|
|
142
142
|
for (const r of e) {
|
|
143
143
|
if (!r.enabled) continue;
|
|
@@ -148,7 +148,7 @@ function C(e, t) {
|
|
|
148
148
|
}
|
|
149
149
|
return n;
|
|
150
150
|
}
|
|
151
|
-
function
|
|
151
|
+
function C(e, t) {
|
|
152
152
|
return t.filter((n) => n.capability.intent === e);
|
|
153
153
|
}
|
|
154
154
|
function i(e, t) {
|
|
@@ -182,28 +182,29 @@ const P = "message_window", V = "message_templates", G = {
|
|
|
182
182
|
// Voice/conference
|
|
183
183
|
HOST: "host",
|
|
184
184
|
PARTICIPANT: "participant"
|
|
185
|
-
}, H = (e, t) => ({ intent: e, ...t }), $ = "folder_management", y = "remote_search", U = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, m = "installation-id";
|
|
185
|
+
}, H = (e, t) => ({ intent: e, ...t }), $ = "folder_management", y = "remote_search", U = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, m = 9e4, h = "installation-id";
|
|
186
186
|
export {
|
|
187
187
|
I as CommunicationScheme,
|
|
188
188
|
$ as FEATURE_FOLDER_MANAGEMENT,
|
|
189
189
|
y as FEATURE_REMOTE_SEARCH,
|
|
190
|
-
|
|
190
|
+
h as INSTALLATION_HEADER,
|
|
191
191
|
G as InteractionParticipantRole,
|
|
192
|
+
m as PRESENCE_ONLINE_WINDOW_MS,
|
|
192
193
|
V as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
|
|
193
194
|
P as PROVIDER_FEATURE_MESSAGE_WINDOW,
|
|
194
195
|
U as UNSUPPORTED,
|
|
195
196
|
_ as buildActivityPreview,
|
|
196
|
-
|
|
197
|
+
O as buildChannelIntents,
|
|
197
198
|
H as defineIntent,
|
|
198
199
|
R as disabledIntentsFromCapabilities,
|
|
199
200
|
M as filterByEndpoint,
|
|
200
|
-
|
|
201
|
+
C as filterByIntent,
|
|
201
202
|
i as filterByTargetScheme,
|
|
202
203
|
g as getContactEndpoints,
|
|
203
204
|
N as getShortTitle,
|
|
204
205
|
L as getUrgencyScore,
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
D as isAssigned,
|
|
207
|
+
f as isClosed,
|
|
207
208
|
S as isInteractionUnseen,
|
|
208
209
|
b as matchContactToIntents,
|
|
209
210
|
c as resolveAccountCapabilities,
|
|
@@ -5,6 +5,13 @@ export interface Session {
|
|
|
5
5
|
providerId: string;
|
|
6
6
|
direction: 'inbound' | 'outbound';
|
|
7
7
|
remoteIdentity: string;
|
|
8
|
+
/**
|
|
9
|
+
* Number of remote participants, when the provider can report it. Used by
|
|
10
|
+
* transcript labeling: with more than one remote party the audio is a single
|
|
11
|
+
* mixed stream, so `remoteIdentity` can't be trusted to name the speaker and
|
|
12
|
+
* the label falls back to a generic one. Absent/1 = single remote party.
|
|
13
|
+
*/
|
|
14
|
+
remoteParticipantCount?: number;
|
|
8
15
|
state: SessionState;
|
|
9
16
|
startTime?: number;
|
|
10
17
|
hasVideo: boolean;
|
|
@@ -287,6 +294,49 @@ export interface RemoteStreamEvent {
|
|
|
287
294
|
speaker?: 'local' | 'remote';
|
|
288
295
|
label?: string;
|
|
289
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Peer-to-peer WebRTC signaling envelope. Exchanged between two users in the
|
|
299
|
+
* same tenant to negotiate a direct call — the media (audio) flows P2P; only
|
|
300
|
+
* these small SDP/ICE setup messages transit the server (relayed 1:1 via
|
|
301
|
+
* `Bridge.sse.push`). Transient: never persisted.
|
|
302
|
+
*
|
|
303
|
+
* - `offer`/`answer` → `data` is an `RTCSessionDescriptionInit`.
|
|
304
|
+
* - `ice` → `data` is an `RTCIceCandidateInit` (or `null` = end-of-candidates).
|
|
305
|
+
* - `bye` → `data` omitted; the sender tore the call down.
|
|
306
|
+
*/
|
|
307
|
+
export type WebRtcSignalKind = "offer" | "answer" | "ice" | "bye" | "request";
|
|
308
|
+
/** Request body the initiator/answerer POSTs to the signaling relay. */
|
|
309
|
+
export interface WebRtcSignalRequest {
|
|
310
|
+
/** Target userId (the peer this signal is meant for). */
|
|
311
|
+
to: string;
|
|
312
|
+
/** Stable call id, minted by the caller on invite. */
|
|
313
|
+
sessionId: string;
|
|
314
|
+
kind: WebRtcSignalKind;
|
|
315
|
+
data?: unknown;
|
|
316
|
+
/** Sender display name, so the peer can label the call UI without a lookup. */
|
|
317
|
+
fromName?: string;
|
|
318
|
+
/**
|
|
319
|
+
* Which peer connection this signal belongs to. `call` (default) is an audio
|
|
320
|
+
* session handled by the transport provider; `file` is an AirDrop-style
|
|
321
|
+
* RTCDataChannel transfer handled by the file-beam service; `monitor` is a
|
|
322
|
+
* one-way "listen along" forward handled by the call-monitor service. Lets
|
|
323
|
+
* one relay serve all three without the connection types colliding.
|
|
324
|
+
*/
|
|
325
|
+
channel?: "call" | "file" | "monitor";
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Broadcast (event `webrtc:callstate`) telling the tenant whether a user is
|
|
329
|
+
* currently on a call, so a supervisor can see who is available to listen in.
|
|
330
|
+
*/
|
|
331
|
+
export interface CallStateChange {
|
|
332
|
+
userId: string;
|
|
333
|
+
inCall: boolean;
|
|
334
|
+
}
|
|
335
|
+
/** What the callee receives over SSE — same envelope, server stamps `from`. */
|
|
336
|
+
export interface WebRtcSignalEvent extends WebRtcSignalRequest {
|
|
337
|
+
/** Sender userId, stamped by the relay from the authenticated subject. */
|
|
338
|
+
from: string;
|
|
339
|
+
}
|
|
290
340
|
/**
|
|
291
341
|
* Helper voor compactere intent-declaraties in `/provider/describe`.
|
|
292
342
|
* defineIntent("mail", { targetSchemes: [MAILTO], transport: { kind: "dispatch" } })
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence — derived, not stored as its own entity. Online status is computed
|
|
3
|
+
* from `InstallationEntity.lastSeenAt`: a user is online when any of their
|
|
4
|
+
* non-revoked installations was seen within the freshness window. A light
|
|
5
|
+
* client heartbeat (~45s while the WebSocket is connected) keeps `lastSeenAt`
|
|
6
|
+
* fresh enough to reflect real reachability for calling.
|
|
7
|
+
*/
|
|
8
|
+
/** How recently a user's last device activity must be to count as online (ms). */
|
|
9
|
+
export declare const PRESENCE_ONLINE_WINDOW_MS = 90000;
|
|
10
|
+
/** One tenant user with their derived online status, as returned by GET /presence. */
|
|
11
|
+
export interface PresenceEntry {
|
|
12
|
+
userId: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
/** Max `lastSeenAt` across the user's non-revoked installations (ms epoch). */
|
|
16
|
+
lastSeenAt: number;
|
|
17
|
+
/** `now - lastSeenAt < PRESENCE_ONLINE_WINDOW_MS` at the time of the read. */
|
|
18
|
+
online: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** SSE payload pushed on an offline->online transition (event `presence:changed`). */
|
|
21
|
+
export interface PresenceChange {
|
|
22
|
+
userId: string;
|
|
23
|
+
online: boolean;
|
|
24
|
+
lastSeenAt: number;
|
|
25
|
+
}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
|
+
import { OwnerScope } from '../entities/contact/types';
|
|
2
|
+
/**
|
|
3
|
+
* A virtual folder: a named, scoped root that binds to a storage backend
|
|
4
|
+
* (a StorageAccount on a storage provider) at a base path. Its `ownerScope`
|
|
5
|
+
* decides who can see and use it (personal / team / org). Files live inside it.
|
|
6
|
+
*
|
|
7
|
+
* Kept named `VirtualMount` for continuity with existing consumers (ui-kit
|
|
8
|
+
* StorageInput, platform-api); surfaced to users as a "folder".
|
|
9
|
+
*/
|
|
1
10
|
export interface VirtualMount {
|
|
2
11
|
id: string;
|
|
12
|
+
organizationId?: string;
|
|
3
13
|
name: string;
|
|
14
|
+
/** Storage provider id (e.g. base64("s3:storage")). */
|
|
4
15
|
providerId: string;
|
|
16
|
+
/** StorageAccount id backing this folder. Falls back to the provider default when omitted. */
|
|
17
|
+
accountId?: string;
|
|
5
18
|
basePath: string;
|
|
6
|
-
|
|
19
|
+
/** Who may see and use this folder. */
|
|
20
|
+
ownerScope: OwnerScope;
|
|
7
21
|
metadata?: Record<string, any>;
|
|
22
|
+
createdAt?: number;
|
|
23
|
+
updatedAt?: number;
|
|
8
24
|
}
|
|
9
25
|
export interface FilePointer {
|
|
10
26
|
id: string;
|
|
@@ -19,15 +35,38 @@ export interface FilePointer {
|
|
|
19
35
|
createdAt?: number;
|
|
20
36
|
tags?: string[];
|
|
21
37
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
/**
|
|
39
|
+
* A field a storage provider needs to connect an account. Providers declare
|
|
40
|
+
* these (via /provider/describe); the accounts UI renders the form from them,
|
|
41
|
+
* so a new backend needs no UI changes.
|
|
42
|
+
*/
|
|
43
|
+
export interface StorageAccountField {
|
|
44
|
+
name: string;
|
|
45
|
+
label: string;
|
|
46
|
+
type: "text" | "password" | "number" | "checkbox";
|
|
47
|
+
required?: boolean;
|
|
48
|
+
placeholder?: string;
|
|
28
49
|
}
|
|
29
|
-
|
|
50
|
+
/** A connectable storage provider + the fields needed to connect an account. */
|
|
51
|
+
export interface StorageProviderInfo {
|
|
52
|
+
/** Storage-role provider id (e.g. base64("ftp:storage")). */
|
|
30
53
|
id: string;
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
displayName: string;
|
|
55
|
+
accountFields: StorageAccountField[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A linked storage backend account (credentials to an S3 bucket, an FTP host, …).
|
|
59
|
+
* Backed by the canonical ManagedAccount store, mirroring the AI app's account
|
|
60
|
+
* pattern. Secrets never reach the UI — this is the public view.
|
|
61
|
+
*/
|
|
62
|
+
export interface StorageAccount {
|
|
63
|
+
id: string;
|
|
64
|
+
organizationId: string;
|
|
65
|
+
/** Account-provider id, e.g. base64("s3:account"). */
|
|
66
|
+
providerId: string;
|
|
67
|
+
displayName: string;
|
|
68
|
+
/** Whether this is the org's default account for its provider. */
|
|
69
|
+
isDefault?: boolean;
|
|
70
|
+
/** Provider-specific, non-secret fields (region, bucket, basePath, …). */
|
|
71
|
+
metadata?: Record<string, any>;
|
|
33
72
|
}
|