@hyperdrive.bot/fleet-server 0.3.99 → 0.3.102
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/server/server/ingestion/composio/gateway.js +6 -1
- package/dist/server/server/ingestion/pipedream/gateway.js +7 -0
- package/dist/server/server/ingestion/service.d.ts +40 -0
- package/dist/server/server/ingestion/service.js +119 -8
- package/dist/server/server/ingestion/types.d.ts +9 -0
- package/dist/server/server/session/ingestion/source-session.d.ts +10 -0
- package/dist/server/server/session/ingestion/source-session.js +25 -0
- package/dist/server/server/session.js +11 -3
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-67f19cbfce758da6aa23c38c2dbd6a33.js → desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js} +5 -5
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-6888615a6b700f975c1f78501ad8577c.js → desktop-attachment-store-7a589090b52127457319ba733fcca36f.js} +3 -3
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7a589090b52127457319ba733fcca36f.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7a589090b52127457319ba733fcca36f.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7a589090b52127457319ba733fcca36f.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7a589090b52127457319ba733fcca36f.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-dc1e81e7cbad84d30a01c48dcc9a5a3e.js → index-ea21f43eebd5b96bf3cb1be6496567ed.js} +30 -29
- package/dist/server/web-ui/_expo/static/js/web/index-ea21f43eebd5b96bf3cb1be6496567ed.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-ea21f43eebd5b96bf3cb1be6496567ed.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-ea21f43eebd5b96bf3cb1be6496567ed.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-dc1e81e7cbad84d30a01c48dcc9a5a3e.js.map.gz → index-ea21f43eebd5b96bf3cb1be6496567ed.js.map.gz} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-991ae7fd00f9fed45084fbb39160e8b8.js → indexeddb-attachment-store-425dc000c40ab14c7de479c8a86e55b3.js} +3 -3
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-425dc000c40ab14c7de479c8a86e55b3.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-425dc000c40ab14c7de479c8a86e55b3.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-425dc000c40ab14c7de479c8a86e55b3.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-425dc000c40ab14c7de479c8a86e55b3.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-fdb4d787a259ce350f76280ca0e2bfa2.js → native-file-attachment-store-5a21541a0e9eb4097726331dae9f2952.js} +5 -5
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-5a21541a0e9eb4097726331dae9f2952.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-5a21541a0e9eb4097726331dae9f2952.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-5a21541a0e9eb4097726331dae9f2952.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-5a21541a0e9eb4097726331dae9f2952.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{profiler-9502a8e721925a6353ec45f7613f537e.js → profiler-d61e032c3f43b3aa19e02fb97c3f7331.js} +11 -11
- package/dist/server/web-ui/_expo/static/js/web/profiler-d61e032c3f43b3aa19e02fb97c3f7331.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-d61e032c3f43b3aa19e02fb97c3f7331.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-d61e032c3f43b3aa19e02fb97c3f7331.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-d61e032c3f43b3aa19e02fb97c3f7331.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{startRecording-4ed5094aa059a0fc724f5ae70dd2f693.js → startRecording-1bb242d6754dee4266ba9a09a252ccea.js} +60 -60
- package/dist/server/web-ui/_expo/static/js/web/startRecording-1bb242d6754dee4266ba9a09a252ccea.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/startRecording-1bb242d6754dee4266ba9a09a252ccea.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/startRecording-1bb242d6754dee4266ba9a09a252ccea.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{startRecording-4ed5094aa059a0fc724f5ae70dd2f693.js.map.gz → startRecording-1bb242d6754dee4266ba9a09a252ccea.js.map.gz} +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/package.json +6 -6
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-67f19cbfce758da6aa23c38c2dbd6a33.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-67f19cbfce758da6aa23c38c2dbd6a33.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-67f19cbfce758da6aa23c38c2dbd6a33.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-67f19cbfce758da6aa23c38c2dbd6a33.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-6888615a6b700f975c1f78501ad8577c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-6888615a6b700f975c1f78501ad8577c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-6888615a6b700f975c1f78501ad8577c.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-6888615a6b700f975c1f78501ad8577c.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-dc1e81e7cbad84d30a01c48dcc9a5a3e.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-dc1e81e7cbad84d30a01c48dcc9a5a3e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-dc1e81e7cbad84d30a01c48dcc9a5a3e.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-991ae7fd00f9fed45084fbb39160e8b8.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-991ae7fd00f9fed45084fbb39160e8b8.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-991ae7fd00f9fed45084fbb39160e8b8.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-991ae7fd00f9fed45084fbb39160e8b8.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-fdb4d787a259ce350f76280ca0e2bfa2.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-fdb4d787a259ce350f76280ca0e2bfa2.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-fdb4d787a259ce350f76280ca0e2bfa2.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-fdb4d787a259ce350f76280ca0e2bfa2.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-9502a8e721925a6353ec45f7613f537e.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-9502a8e721925a6353ec45f7613f537e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-9502a8e721925a6353ec45f7613f537e.js.map.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/profiler-9502a8e721925a6353ec45f7613f537e.js.map.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/startRecording-4ed5094aa059a0fc724f5ae70dd2f693.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/startRecording-4ed5094aa059a0fc724f5ae70dd2f693.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/startRecording-4ed5094aa059a0fc724f5ae70dd2f693.js.map.br +0 -0
|
@@ -375,7 +375,12 @@ export function createComposioGateway(config) {
|
|
|
375
375
|
if (!item.id) {
|
|
376
376
|
continue;
|
|
377
377
|
}
|
|
378
|
-
|
|
378
|
+
// Composio's connected-account payload carries no email or display
|
|
379
|
+
// name for the authorized account - `user_id` is the caller-supplied
|
|
380
|
+
// external id, which is the daemon's own tenant string here and so
|
|
381
|
+
// identifies nothing about the user. Null rather than a value that
|
|
382
|
+
// would look like an identity and not be one.
|
|
383
|
+
accounts.push({ id: item.id, status: normalizeStatus(item.status), label: null });
|
|
379
384
|
}
|
|
380
385
|
return accounts;
|
|
381
386
|
},
|
|
@@ -181,6 +181,13 @@ export function createPipedreamGateway(config) {
|
|
|
181
181
|
accounts.push({
|
|
182
182
|
id: account.id,
|
|
183
183
|
status: account.healthy === false ? "inactive" : "active",
|
|
184
|
+
// Pipedream's `name` is the account's custom name, which for an OAuth
|
|
185
|
+
// app is normally the address the user authorized with. It is
|
|
186
|
+
// optional in the SDK type, so it is coalesced rather than assumed -
|
|
187
|
+
// an absent label renders as "Not linked to an account", never as the
|
|
188
|
+
// opaque `apn_...` id, which would be an identifier pretending to be
|
|
189
|
+
// an identity.
|
|
190
|
+
label: account.name ?? null,
|
|
184
191
|
});
|
|
185
192
|
}
|
|
186
193
|
return accounts;
|
|
@@ -66,6 +66,15 @@ export declare class IngestionService {
|
|
|
66
66
|
*/
|
|
67
67
|
connect(kind: SourceKind, appSlug: string): Promise<SourceConnectResult>;
|
|
68
68
|
list(): Promise<SourceSummary[]>;
|
|
69
|
+
/**
|
|
70
|
+
* sourceId -> number of filters watching it.
|
|
71
|
+
*
|
|
72
|
+
* Best-effort: a filter store that cannot be read must not blank the sources
|
|
73
|
+
* list, because the count is a decoration on a row whose real subject is the
|
|
74
|
+
* connection. Degrades to an empty map, so every row reports zero rather than
|
|
75
|
+
* the whole screen reporting nothing.
|
|
76
|
+
*/
|
|
77
|
+
private filterCounts;
|
|
69
78
|
/** Check one source and persist the verdict. Null when the source is gone. */
|
|
70
79
|
check(sourceId: string): Promise<SourceSummary | null>;
|
|
71
80
|
private hasConfiguredGateway;
|
|
@@ -137,6 +146,37 @@ export declare class IngestionService {
|
|
|
137
146
|
* uniformly cheerful `true`.
|
|
138
147
|
*/
|
|
139
148
|
deleteFilter(filterId: string): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* The catalogue's own name and icon for an app slug.
|
|
151
|
+
*
|
|
152
|
+
* Best-effort by design: a catalogue call that fails or returns no match must
|
|
153
|
+
* not fail a connect whose OAuth handoff already succeeded, so it degrades to
|
|
154
|
+
* exactly what this method replaced (the raw slug, no icon) rather than
|
|
155
|
+
* throwing. `imgSrc` stays `""` in that case, matching what every source
|
|
156
|
+
* written before this change already holds on disk.
|
|
157
|
+
*/
|
|
158
|
+
private resolvePresentation;
|
|
159
|
+
/**
|
|
160
|
+
* Forget a source and every filter bound to it.
|
|
161
|
+
*
|
|
162
|
+
* LOCAL ONLY. The aggregator keeps the grant, so this is "paseo stops
|
|
163
|
+
* watching", not "the provider forgot you" - and the confirm copy must say
|
|
164
|
+
* that, because promising a revoke paseo does not perform is the same class
|
|
165
|
+
* of lie as the connect sheet's old "it updates on its own".
|
|
166
|
+
*
|
|
167
|
+
* Filters CASCADE rather than being orphaned. A filter whose `sourceId` no
|
|
168
|
+
* longer resolves is not inert: it resolves to `SOURCE_MISSING` and shows up
|
|
169
|
+
* as a broken row forever, so leaving them would trade one piece of
|
|
170
|
+
* unremovable state for another.
|
|
171
|
+
*
|
|
172
|
+
* The in-flight connect poll is cancelled FIRST. It writes health on a timer,
|
|
173
|
+
* so deleting the record underneath a live poll would let it recreate state
|
|
174
|
+
* for a source the user just removed.
|
|
175
|
+
*/
|
|
176
|
+
disconnect(sourceId: string): Promise<{
|
|
177
|
+
removed: boolean;
|
|
178
|
+
removedFilterIds: string[];
|
|
179
|
+
}>;
|
|
140
180
|
/**
|
|
141
181
|
* Field by field on purpose. A `...stored` spread would mean a field added to
|
|
142
182
|
* StoredSource later reaches the wire the day it is added, with no diff on
|
|
@@ -104,13 +104,25 @@ export class IngestionService {
|
|
|
104
104
|
const gateway = this.resolveGateway(kind);
|
|
105
105
|
const handoff = await gateway.connect(this.externalUserId, appSlug);
|
|
106
106
|
const timestamp = this.now().toISOString();
|
|
107
|
+
// The catalogue already knows this app as "Google Calendar" with a real
|
|
108
|
+
// icon, and the client rendered exactly that one tap earlier. Writing the
|
|
109
|
+
// raw slug threw both away at the moment of persisting, so every row read
|
|
110
|
+
// "google_calendar" with no icon forever after.
|
|
111
|
+
//
|
|
112
|
+
// Resolved daemon-side rather than accepted from the client: a client that
|
|
113
|
+
// could supply displayName and imgSrc could write the strings the row
|
|
114
|
+
// renders, and store.test.ts already guards against exactly that shape.
|
|
115
|
+
// Best-effort - a catalogue hiccup must not fail a connect that otherwise
|
|
116
|
+
// succeeded, so it falls back to the previous behaviour.
|
|
117
|
+
const presentation = await this.resolvePresentation(gateway, appSlug);
|
|
107
118
|
const source = await this.store.create({
|
|
108
119
|
kind,
|
|
109
120
|
appSlug,
|
|
110
|
-
displayName:
|
|
111
|
-
imgSrc:
|
|
121
|
+
displayName: presentation.displayName,
|
|
122
|
+
imgSrc: presentation.imgSrc,
|
|
112
123
|
externalUserId: this.externalUserId,
|
|
113
124
|
externalAccountId: null,
|
|
125
|
+
accountLabel: null,
|
|
114
126
|
status: "connecting",
|
|
115
127
|
createdAt: timestamp,
|
|
116
128
|
updatedAt: timestamp,
|
|
@@ -118,11 +130,37 @@ export class IngestionService {
|
|
|
118
130
|
lastError: null,
|
|
119
131
|
});
|
|
120
132
|
this.startConnectPoll(source);
|
|
121
|
-
|
|
133
|
+
// 0 is correct and not a placeholder: a source created moments ago cannot
|
|
134
|
+
// have a filter bound to it yet.
|
|
135
|
+
return { source: this.toSummary(source, 0), redirectUrl: handoff.redirectUrl };
|
|
122
136
|
}
|
|
123
137
|
async list() {
|
|
124
138
|
const sources = await this.store.list();
|
|
125
|
-
|
|
139
|
+
// ONE filter read for the whole list, not one per source. `list()` is
|
|
140
|
+
// readdir + parse of every filter file, so calling it inside the map would
|
|
141
|
+
// make a nine-source list do nine full scans of the same directory.
|
|
142
|
+
const counts = await this.filterCounts();
|
|
143
|
+
return sources.map((source) => this.toSummary(source, counts.get(source.id) ?? 0));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* sourceId -> number of filters watching it.
|
|
147
|
+
*
|
|
148
|
+
* Best-effort: a filter store that cannot be read must not blank the sources
|
|
149
|
+
* list, because the count is a decoration on a row whose real subject is the
|
|
150
|
+
* connection. Degrades to an empty map, so every row reports zero rather than
|
|
151
|
+
* the whole screen reporting nothing.
|
|
152
|
+
*/
|
|
153
|
+
async filterCounts() {
|
|
154
|
+
const counts = new Map();
|
|
155
|
+
try {
|
|
156
|
+
for (const filter of await this.filterStore.list()) {
|
|
157
|
+
counts.set(filter.sourceId, (counts.get(filter.sourceId) ?? 0) + 1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.logger.warn({ err: error }, "Filter count unavailable; reporting zero per source");
|
|
162
|
+
}
|
|
163
|
+
return counts;
|
|
126
164
|
}
|
|
127
165
|
/** Check one source and persist the verdict. Null when the source is gone. */
|
|
128
166
|
async check(sourceId) {
|
|
@@ -132,7 +170,14 @@ export class IngestionService {
|
|
|
132
170
|
}
|
|
133
171
|
const health = await checkSource(source, this.healthDeps());
|
|
134
172
|
const updated = await this.persistHealth(sourceId, health);
|
|
135
|
-
|
|
173
|
+
if (!updated) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// Counted, not defaulted: `check` returns an existing source, and reporting
|
|
177
|
+
// zero filters for a source that has some would let a re-check silently
|
|
178
|
+
// blank the row's work line.
|
|
179
|
+
const counts = await this.filterCounts();
|
|
180
|
+
return this.toSummary(updated, counts.get(updated.id) ?? 0);
|
|
136
181
|
}
|
|
137
182
|
hasConfiguredGateway() {
|
|
138
183
|
return SOURCE_CAPABLE_KINDS.some((kind) => this.resolveGateway(kind).configured);
|
|
@@ -205,7 +250,7 @@ export class IngestionService {
|
|
|
205
250
|
}
|
|
206
251
|
const active = accounts.find((account) => account.status === "active");
|
|
207
252
|
if (active) {
|
|
208
|
-
await this.persistHealth(source.id, { status: "active", lastError: null }, active.id);
|
|
253
|
+
await this.persistHealth(source.id, { status: "active", lastError: null }, active.id, active.label);
|
|
209
254
|
return;
|
|
210
255
|
}
|
|
211
256
|
await this.sleep(CONNECT_POLL_INTERVAL_MS, state);
|
|
@@ -254,7 +299,7 @@ export class IngestionService {
|
|
|
254
299
|
* Re-reads the record first so a source deleted while a poll or a sweep was
|
|
255
300
|
* in flight is not resurrected by a stale write.
|
|
256
301
|
*/
|
|
257
|
-
async persistHealth(sourceId, health, externalAccountId) {
|
|
302
|
+
async persistHealth(sourceId, health, externalAccountId, accountLabel) {
|
|
258
303
|
const current = await this.store.get(sourceId);
|
|
259
304
|
if (!current) {
|
|
260
305
|
return null;
|
|
@@ -267,6 +312,7 @@ export class IngestionService {
|
|
|
267
312
|
lastCheckedAt: timestamp,
|
|
268
313
|
updatedAt: timestamp,
|
|
269
314
|
...(externalAccountId !== undefined ? { externalAccountId } : {}),
|
|
315
|
+
...(accountLabel !== undefined ? { accountLabel } : {}),
|
|
270
316
|
};
|
|
271
317
|
await this.store.put(next);
|
|
272
318
|
return next;
|
|
@@ -340,12 +386,75 @@ export class IngestionService {
|
|
|
340
386
|
await this.filterStore.delete(filterId);
|
|
341
387
|
return true;
|
|
342
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* The catalogue's own name and icon for an app slug.
|
|
391
|
+
*
|
|
392
|
+
* Best-effort by design: a catalogue call that fails or returns no match must
|
|
393
|
+
* not fail a connect whose OAuth handoff already succeeded, so it degrades to
|
|
394
|
+
* exactly what this method replaced (the raw slug, no icon) rather than
|
|
395
|
+
* throwing. `imgSrc` stays `""` in that case, matching what every source
|
|
396
|
+
* written before this change already holds on disk.
|
|
397
|
+
*/
|
|
398
|
+
async resolvePresentation(gateway, appSlug) {
|
|
399
|
+
try {
|
|
400
|
+
const page = await gateway.listSources(appSlug);
|
|
401
|
+
const match = page.items.find((entry) => entry.slug === appSlug);
|
|
402
|
+
if (match) {
|
|
403
|
+
return { displayName: match.name, imgSrc: match.imgSrc };
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
// Logged, never swallowed silently: if this degrades in production the
|
|
408
|
+
// symptom is a row that says "google_calendar", and a bare catch would
|
|
409
|
+
// make that indistinguishable from an aggregator that simply has no
|
|
410
|
+
// catalogue entry.
|
|
411
|
+
this.logger.warn({ err: error, appSlug }, "Catalogue lookup failed; falling back to slug");
|
|
412
|
+
}
|
|
413
|
+
return { displayName: appSlug, imgSrc: "" };
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Forget a source and every filter bound to it.
|
|
417
|
+
*
|
|
418
|
+
* LOCAL ONLY. The aggregator keeps the grant, so this is "paseo stops
|
|
419
|
+
* watching", not "the provider forgot you" - and the confirm copy must say
|
|
420
|
+
* that, because promising a revoke paseo does not perform is the same class
|
|
421
|
+
* of lie as the connect sheet's old "it updates on its own".
|
|
422
|
+
*
|
|
423
|
+
* Filters CASCADE rather than being orphaned. A filter whose `sourceId` no
|
|
424
|
+
* longer resolves is not inert: it resolves to `SOURCE_MISSING` and shows up
|
|
425
|
+
* as a broken row forever, so leaving them would trade one piece of
|
|
426
|
+
* unremovable state for another.
|
|
427
|
+
*
|
|
428
|
+
* The in-flight connect poll is cancelled FIRST. It writes health on a timer,
|
|
429
|
+
* so deleting the record underneath a live poll would let it recreate state
|
|
430
|
+
* for a source the user just removed.
|
|
431
|
+
*/
|
|
432
|
+
async disconnect(sourceId) {
|
|
433
|
+
const existing = await this.store.get(sourceId);
|
|
434
|
+
if (existing === null) {
|
|
435
|
+
return { removed: false, removedFilterIds: [] };
|
|
436
|
+
}
|
|
437
|
+
// Cancel BEFORE deleting. The poll writes health on a 2s timer, so
|
|
438
|
+
// removing the record underneath a live poll lets it persist state for a
|
|
439
|
+
// source the user just removed. `cancel()` also wakes the sleep, so the
|
|
440
|
+
// loop exits at its next check rather than after the full interval; the
|
|
441
|
+
// poll's own `finally` removes the map entry.
|
|
442
|
+
this.connectPolls.get(sourceId)?.cancel();
|
|
443
|
+
const filters = await this.filterStore.list();
|
|
444
|
+
const bound = filters.filter((filter) => filter.sourceId === sourceId);
|
|
445
|
+
for (const filter of bound) {
|
|
446
|
+
await this.filterStore.delete(filter.id);
|
|
447
|
+
}
|
|
448
|
+
await this.store.delete(sourceId);
|
|
449
|
+
this.logger.info({ sourceId, removedFilters: bound.length }, "Source disconnected and its filters removed");
|
|
450
|
+
return { removed: true, removedFilterIds: bound.map((filter) => filter.id) };
|
|
451
|
+
}
|
|
343
452
|
/**
|
|
344
453
|
* Field by field on purpose. A `...stored` spread would mean a field added to
|
|
345
454
|
* StoredSource later reaches the wire the day it is added, with no diff on
|
|
346
455
|
* this file to review.
|
|
347
456
|
*/
|
|
348
|
-
toSummary(source) {
|
|
457
|
+
toSummary(source, filterCount = 0) {
|
|
349
458
|
return {
|
|
350
459
|
id: source.id,
|
|
351
460
|
kind: source.kind,
|
|
@@ -353,11 +462,13 @@ export class IngestionService {
|
|
|
353
462
|
displayName: source.displayName,
|
|
354
463
|
imgSrc: source.imgSrc,
|
|
355
464
|
externalAccountId: source.externalAccountId,
|
|
465
|
+
accountLabel: source.accountLabel,
|
|
356
466
|
status: source.status,
|
|
357
467
|
createdAt: source.createdAt,
|
|
358
468
|
updatedAt: source.updatedAt,
|
|
359
469
|
lastCheckedAt: source.lastCheckedAt,
|
|
360
470
|
lastError: source.lastError,
|
|
471
|
+
filterCount,
|
|
361
472
|
};
|
|
362
473
|
}
|
|
363
474
|
}
|
|
@@ -55,6 +55,15 @@ export interface SourceConnectHandoff {
|
|
|
55
55
|
export interface SourceAccount {
|
|
56
56
|
id: string;
|
|
57
57
|
status: "active" | "pending" | "inactive";
|
|
58
|
+
/**
|
|
59
|
+
* The human name of the account, when the aggregator gives one - typically
|
|
60
|
+
* the email for an OAuth app. Null when it does not.
|
|
61
|
+
*
|
|
62
|
+
* Both gateways already receive this on a call they already make and drop it
|
|
63
|
+
* on the floor, which is why a connected row could only ever say "gmail" and
|
|
64
|
+
* never WHICH gmail.
|
|
65
|
+
*/
|
|
66
|
+
label: string | null;
|
|
58
67
|
}
|
|
59
68
|
export interface PipedreamConfig {
|
|
60
69
|
clientId?: string | undefined;
|
|
@@ -74,5 +74,15 @@ export declare class SourceSession {
|
|
|
74
74
|
handleIngestionFilterDeleteRequest(request: Extract<SessionInboundMessage, {
|
|
75
75
|
type: "ingestion/filter/delete";
|
|
76
76
|
}>): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Forget a source and the filters bound to it.
|
|
79
|
+
*
|
|
80
|
+
* `removed: false` with `error: null` says the id resolved to nothing, which
|
|
81
|
+
* is an answer: disconnecting twice is idempotent, and reporting the second
|
|
82
|
+
* call as a failure would make a double-tap look like a broken daemon.
|
|
83
|
+
*/
|
|
84
|
+
handleSourceDisconnectRequest(request: Extract<SessionInboundMessage, {
|
|
85
|
+
type: "source/disconnect";
|
|
86
|
+
}>): Promise<void>;
|
|
77
87
|
}
|
|
78
88
|
//# sourceMappingURL=source-session.d.ts.map
|
|
@@ -187,5 +187,30 @@ export class SourceSession {
|
|
|
187
187
|
this.emitSourceRpcError(request, error);
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Forget a source and the filters bound to it.
|
|
192
|
+
*
|
|
193
|
+
* `removed: false` with `error: null` says the id resolved to nothing, which
|
|
194
|
+
* is an answer: disconnecting twice is idempotent, and reporting the second
|
|
195
|
+
* call as a failure would make a double-tap look like a broken daemon.
|
|
196
|
+
*/
|
|
197
|
+
async handleSourceDisconnectRequest(request) {
|
|
198
|
+
try {
|
|
199
|
+
const result = await this.ingestionService.disconnect(request.sourceId);
|
|
200
|
+
this.host.emit({
|
|
201
|
+
type: "source/disconnect/response",
|
|
202
|
+
payload: {
|
|
203
|
+
requestId: request.requestId,
|
|
204
|
+
sourceId: request.sourceId,
|
|
205
|
+
removed: result.removed,
|
|
206
|
+
removedFilterIds: result.removedFilterIds,
|
|
207
|
+
error: null,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
this.emitSourceRpcError(request, error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
190
215
|
}
|
|
191
216
|
//# sourceMappingURL=source-session.js.map
|
|
@@ -1747,6 +1747,8 @@ export class Session {
|
|
|
1747
1747
|
return this.sourceSession.handleSourceListRequest(msg);
|
|
1748
1748
|
case "source/check":
|
|
1749
1749
|
return this.sourceSession.handleSourceCheckRequest(msg);
|
|
1750
|
+
case "source/disconnect":
|
|
1751
|
+
return this.sourceSession.handleSourceDisconnectRequest(msg);
|
|
1750
1752
|
// Epic 7 filter authoring. Routed to SourceSession rather than
|
|
1751
1753
|
// ChatIngestionSession because these need no chat context: they are
|
|
1752
1754
|
// store CRUD, and SourceSession is already the source-scoped surface.
|
|
@@ -3420,9 +3422,15 @@ export class Session {
|
|
|
3420
3422
|
if (!normalized.cwd) {
|
|
3421
3423
|
throw new Error("Import requires cwd from the selected provider session");
|
|
3422
3424
|
}
|
|
3423
|
-
//
|
|
3424
|
-
//
|
|
3425
|
-
|
|
3425
|
+
// Look before you mint. This used to call createWorkspaceForDirectory
|
|
3426
|
+
// unconditionally, so importing a session whose cwd ALREADY had a workspace
|
|
3427
|
+
// produced a second one and the imported agent surfaced there instead of in
|
|
3428
|
+
// the workspace on screen. Same class as the palette (0.3.38),
|
|
3429
|
+
// resolveOrCreateWorkspaceIdForCreateAgent (MR !44) and
|
|
3430
|
+
// ensureWorkspaceForCreate (MR !80); import was the last call site still
|
|
3431
|
+
// minting. findOrCreateWorkspaceForDirectory keeps the never-seen-cwd case
|
|
3432
|
+
// minting, which is the only case where a new workspace is correct.
|
|
3433
|
+
const workspace = await this.workspaceProvisioning.findOrCreateWorkspaceForDirectory(normalized.cwd);
|
|
3426
3434
|
const { snapshot, timelineSize } = await importProviderSession({
|
|
3427
3435
|
request: normalized,
|
|
3428
3436
|
workspaceId: workspace.workspaceId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},
|
|
2
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.writeDesktopAttachmentBase64=async function(n){return await(0,t.invokeDesktopCommand)("write_attachment_base64",{attachmentId:n.attachmentId,base64:n.base64,extension:n.extension??null})},e.writeDesktopAttachmentBytes=async function(n){return await(0,t.invokeDesktopCommand)("write_attachment_bytes",{attachmentId:n.attachmentId,bytes:n.bytes,extension:n.extension??null})},e.copyDesktopAttachmentFile=async function(n){return await(0,t.invokeDesktopCommand)("copy_attachment_file",{attachmentId:n.attachmentId,sourcePath:n.sourcePath,extension:n.extension??null})},e.deleteDesktopAttachmentFile=async function(n){return await(0,t.invokeDesktopCommand)("delete_attachment_file",{path:n.path})},e.garbageCollectDesktopAttachmentFiles=async function(n){return await(0,t.invokeDesktopCommand)("garbage_collect_attachment_files",{referencedIds:[...n.referencedIds]})};var t=r(d[0])},
|
|
3
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.readDesktopFileBase64=s,e.createDesktopFileReader=function(){return{readFileBase64:s}},e.createBrowserObjectUrlMinter=function(){return{tryCreate({mimeType:t,base64:n}){if("undefined"==typeof URL||"function"!=typeof URL.createObjectURL)return null;const o=c(n),s=new ArrayBuffer(o.byteLength);new Uint8Array(s).set(o);const u=new Blob([s],{type:t});return URL.createObjectURL(u)},revoke(t){"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(t)}}},e.createDesktopPreviewUrlResolver=function(t){const n=new Set;return{async resolve(o){const s=await t.reader.readFileBase64(o.storageKey),c=t.objectUrls.tryCreate({mimeType:o.mimeType,base64:s});return null===c?`data:${o.mimeType};base64,${s}`:(n.add(c),c)},async release({url:o}){n.has(o)&&(n.delete(o),t.objectUrls.revoke(o))}}};var t=r(d[0]),n=r(d[1]),o=r(d[2]);async function s(o){return await(0,n.invokeDesktopCommand)("read_file_base64",{path:(0,t.fileUriToPath)(o)})}function c(t){return new Uint8Array(o.Buffer.from(t,"base64"))}},
|
|
4
|
-
//# sourceMappingURL=/_expo/static/js/web/desktop-attachment-bridge-
|
|
5
|
-
//# debugId=
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},4922,[4927,4928]);
|
|
2
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.writeDesktopAttachmentBase64=async function(n){return await(0,t.invokeDesktopCommand)("write_attachment_base64",{attachmentId:n.attachmentId,base64:n.base64,extension:n.extension??null})},e.writeDesktopAttachmentBytes=async function(n){return await(0,t.invokeDesktopCommand)("write_attachment_bytes",{attachmentId:n.attachmentId,bytes:n.bytes,extension:n.extension??null})},e.copyDesktopAttachmentFile=async function(n){return await(0,t.invokeDesktopCommand)("copy_attachment_file",{attachmentId:n.attachmentId,sourcePath:n.sourcePath,extension:n.extension??null})},e.deleteDesktopAttachmentFile=async function(n){return await(0,t.invokeDesktopCommand)("delete_attachment_file",{path:n.path})},e.garbageCollectDesktopAttachmentFiles=async function(n){return await(0,t.invokeDesktopCommand)("garbage_collect_attachment_files",{referencedIds:[...n.referencedIds]})};var t=r(d[0])},4927,[3419]);
|
|
3
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.readDesktopFileBase64=s,e.createDesktopFileReader=function(){return{readFileBase64:s}},e.createBrowserObjectUrlMinter=function(){return{tryCreate({mimeType:t,base64:n}){if("undefined"==typeof URL||"function"!=typeof URL.createObjectURL)return null;const o=c(n),s=new ArrayBuffer(o.byteLength);new Uint8Array(s).set(o);const u=new Blob([s],{type:t});return URL.createObjectURL(u)},revoke(t){"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(t)}}},e.createDesktopPreviewUrlResolver=function(t){const n=new Set;return{async resolve(o){const s=await t.reader.readFileBase64(o.storageKey),c=t.objectUrls.tryCreate({mimeType:o.mimeType,base64:s});return null===c?`data:${o.mimeType};base64,${s}`:(n.add(c),c)},async release({url:o}){n.has(o)&&(n.delete(o),t.objectUrls.revoke(o))}}};var t=r(d[0]),n=r(d[1]),o=r(d[2]);async function s(o){return await(0,n.invokeDesktopCommand)("read_file_base64",{path:(0,t.fileUriToPath)(o)})}function c(t){return new Uint8Array(o.Buffer.from(t,"base64"))}},4928,[4593,3419,52]);
|
|
4
|
+
//# sourceMappingURL=/_expo/static/js/web/desktop-attachment-bridge-6beae08a69e097c7f552119696c67413.js.map
|
|
5
|
+
//# debugId=8d0f2f2e-6216-48e2-87c5-51b8c02d5b57
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},
|
|
2
|
-
//# sourceMappingURL=/_expo/static/js/web/desktop-attachment-store-
|
|
3
|
-
//# debugId=
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},4921,[4593]);
|
|
2
|
+
//# sourceMappingURL=/_expo/static/js/web/desktop-attachment-store-7a589090b52127457319ba733fcca36f.js.map
|
|
3
|
+
//# debugId=950ba5c5-7992-4432-ad49-c7f52c12d7e4
|
|
Binary file
|
|
Binary file
|
|
Binary file
|