@aglyn/plugins-workflows 1.0.0-beta.179 → 1.0.0-beta.181

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 (52) hide show
  1. package/package.json +12 -11
  2. package/src/lib/components/automation-trigger-conditions.component.d.ts +43 -0
  3. package/src/lib/components/automation-trigger-conditions.component.js +189 -0
  4. package/src/lib/components/automation-trigger-conditions.component.js.map +1 -0
  5. package/src/lib/components/host-actions-card.component.js +27 -176
  6. package/src/lib/components/host-actions-card.component.js.map +1 -1
  7. package/src/lib/components/org-automation-editor.component.d.ts +51 -0
  8. package/src/lib/components/org-automation-editor.component.js +310 -0
  9. package/src/lib/components/org-automation-editor.component.js.map +1 -0
  10. package/src/lib/components/org-automations-card.component.d.ts +28 -0
  11. package/src/lib/components/org-automations-card.component.js +373 -0
  12. package/src/lib/components/org-automations-card.component.js.map +1 -0
  13. package/src/lib/components/org-site-automation-list.component.d.ts +43 -0
  14. package/src/lib/components/org-site-automation-list.component.js +289 -0
  15. package/src/lib/components/org-site-automation-list.component.js.map +1 -0
  16. package/src/lib/components/site-org-automations-panel.component.d.ts +22 -0
  17. package/src/lib/components/site-org-automations-panel.component.js +201 -0
  18. package/src/lib/components/site-org-automations-panel.component.js.map +1 -0
  19. package/src/lib/components/use-org-automation-step-pickers.d.ts +20 -0
  20. package/src/lib/components/use-org-automation-step-pickers.js +108 -0
  21. package/src/lib/components/use-org-automation-step-pickers.js.map +1 -0
  22. package/src/lib/components/use-org-automations-api.d.ts +16 -0
  23. package/src/lib/components/use-org-automations-api.js +55 -0
  24. package/src/lib/components/use-org-automations-api.js.map +1 -0
  25. package/src/lib/components/workflows-console-page.js +63 -1
  26. package/src/lib/components/workflows-console-page.js.map +1 -1
  27. package/src/lib/components/workflows-console-sections.d.ts +19 -1
  28. package/src/lib/components/workflows-console-sections.js +37 -2
  29. package/src/lib/components/workflows-console-sections.js.map +1 -1
  30. package/src/lib/components/workflows-org-mount.d.ts +52 -0
  31. package/src/lib/components/workflows-org-mount.js +49 -0
  32. package/src/lib/components/workflows-org-mount.js.map +1 -0
  33. package/src/lib/engine/flow-enrollments.d.ts +26 -10
  34. package/src/lib/engine/flow-enrollments.js +14 -7
  35. package/src/lib/engine/flow-enrollments.js.map +1 -1
  36. package/src/lib/engine/run-event-actions.d.ts +57 -4
  37. package/src/lib/engine/run-event-actions.js +119 -19
  38. package/src/lib/engine/run-event-actions.js.map +1 -1
  39. package/src/lib/engine/run-org-automations.d.ts +75 -0
  40. package/src/lib/engine/run-org-automations.js +154 -0
  41. package/src/lib/engine/run-org-automations.js.map +1 -0
  42. package/src/lib/model/org-automations.d.ts +232 -0
  43. package/src/lib/model/org-automations.js +423 -0
  44. package/src/lib/model/org-automations.js.map +1 -0
  45. package/src/lib/plugin.js +26 -1
  46. package/src/lib/plugin.js.map +1 -1
  47. package/src/lib/server/org-automations-routes.d.ts +88 -0
  48. package/src/lib/server/org-automations-routes.js +474 -0
  49. package/src/lib/server/org-automations-routes.js.map +1 -0
  50. package/src/lib/server-console.d.ts +2 -1
  51. package/src/lib/server-console.js +12 -2
  52. package/src/lib/server-console.js.map +1 -1
@@ -0,0 +1,474 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ /**
18
+ * WRITING AN ORG AUTOMATION, and pausing one on a site (AGL-3302).
19
+ *
20
+ * `orgs/{orgId}/automations` is closed to every client write, the way the
21
+ * outreach collections are: an org automation runs on every site it is
22
+ * placed on, as that site, so its trigger, its steps and its placement are
23
+ * decisions the server checks — the vocabulary, the organization's sites, the
24
+ * plan — and nothing a browser may write around.
25
+ *
26
+ * ## Two doors
27
+ *
28
+ * `automations/manage` is the organization's: it creates, edits, switches on
29
+ * and off, and deletes. It names the org and NO site, so it declares a
30
+ * subject resolver for the dispatchers' release gate and asks the org's
31
+ * plugin switch itself; the caller must be an org-wide owner, admin or
32
+ * editor.
33
+ *
34
+ * `automations/pause` is the SITE's — the host level control. It names the
35
+ * site, so the dispatcher applies that site's plugin switch as for any site
36
+ * request, and it changes one thing: whether this automation is paused on
37
+ * THIS site. The site's own admins and editors may use it, and so may an
38
+ * org-wide editor; nobody may pause a site through it but the one named.
39
+ */ import { createResourceUid } from "@aglyn/aglyn/app-utils/create-resource-uid";
40
+ import { hostIdsFromScope, visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
41
+ import { isPluginEnabled } from "@aglyn/aglyn/plugin-manager/enabled-plugins";
42
+ import { checkEntitlement, isOrgWideMember, planLabelGrantingFeature } from "@aglyn/aglyn/server";
43
+ /*
44
+ * The MODULES, not the barrel: `@aglyn/tenant-data-admin`'s index reaches the
45
+ * Next render cache, and everything these doors need is the Admin SDK handle,
46
+ * the id predicate and the org reads.
47
+ */ import { isDocumentId } from "@aglyn/tenant-data-admin/server/document-id";
48
+ import firebaseAdmin from "@aglyn/tenant-data-admin/server/firebase-admin";
49
+ import { getOrgDoc, logHostActivity, logOrgActivity, resolveOrgIdForHost, resolveOrgMembership } from "@aglyn/tenant-data-admin/server/organizations";
50
+ import { FieldValue } from "firebase-admin/firestore";
51
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
52
+ import { ORG_AUTOMATION_ACTIVITY_TARGET, ORG_AUTOMATION_API_ROUTES, ORG_AUTOMATIONS_COLLECTION, ORG_AUTOMATIONS_MAX, orgAutomationPausedHostIds, prunePausedHostIds, readOrgAutomation } from "../model/org-automations.js";
53
+ export { ORG_AUTOMATION_API_ROUTES };
54
+ /** What `automations/manage` may be asked to do. */ export const ORG_AUTOMATION_MANAGE_ACTIONS = [
55
+ 'create',
56
+ 'update',
57
+ 'setEnabled',
58
+ 'delete'
59
+ ];
60
+ /** The org roles that may write the organization's automations. */ const ORG_MANAGE_ROLES = [
61
+ 'owner',
62
+ 'admin',
63
+ 'editor'
64
+ ];
65
+ /** The site roles that may pause an org automation on their own site. */ const SITE_PAUSE_ROLES = [
66
+ 'admin',
67
+ 'editor'
68
+ ];
69
+ function defaultDeps() {
70
+ return {
71
+ firestore: ()=>firebaseAdmin.app().firestore(),
72
+ verifyIdToken: async (token)=>{
73
+ var _decoded_email;
74
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(token);
75
+ return {
76
+ uid: decoded.uid,
77
+ email: (_decoded_email = decoded.email) != null ? _decoded_email : null
78
+ };
79
+ },
80
+ resolveOrgMembership: async (uid, orgId)=>await resolveOrgMembership(uid, orgId),
81
+ getOrgDoc: async (orgId)=>{
82
+ var _ref;
83
+ return (_ref = await getOrgDoc(orgId)) != null ? _ref : null;
84
+ },
85
+ resolveOrgIdForHost,
86
+ logOrgActivity: (orgId, actor, action, target)=>logOrgActivity(orgId, actor, action, target),
87
+ logHostActivity: (hostId, actor, action, target)=>logHostActivity(hostId, actor, action, target),
88
+ newId: createResourceUid
89
+ };
90
+ }
91
+ /** The bearer token a request carries, or null. */ function bearerToken(headers) {
92
+ var _headers_authorization;
93
+ const authorization = String((_headers_authorization = headers['authorization']) != null ? _headers_authorization : '');
94
+ return authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) || null : null;
95
+ }
96
+ function automationsOf(firestore, orgId) {
97
+ return firestore.collection('orgs').doc(orgId).collection(ORG_AUTOMATIONS_COLLECTION);
98
+ }
99
+ /**
100
+ * Whether every site a placement names belongs to this organization — asked
101
+ * of `hostIndex`, the mirror `resolveOrgIdForHost` reads, in one round trip.
102
+ * `['org']` names no site and always answers yes.
103
+ */ async function placementIsTheOrgs(firestore, orgId, visibleTo) {
104
+ const hostIds = hostIdsFromScope(visibleTo);
105
+ if (!hostIds.length) return true;
106
+ if (!hostIds.every(isDocumentId)) return false;
107
+ const entries = await firestore.getAll(...hostIds.map((hostId)=>firestore.collection('hostIndex').doc(hostId)));
108
+ return entries.every((entry)=>entry.exists && entry.get('orgId') === orgId);
109
+ }
110
+ /**
111
+ * The organization a `automations/manage` request is for, read off its JSON
112
+ * body, so the dispatchers' release gate asks about the right organization.
113
+ * Consulted only for a request that names no site. Unverified, exactly as a
114
+ * `hostId` is: the handler refuses anyone who is not an org-wide member.
115
+ */ export async function orgAutomationsManageSubject(request) {
116
+ if (request.method !== 'POST') return null;
117
+ const body = await request.json().catch(()=>null);
118
+ const orgId = typeof (body == null ? void 0 : body.orgId) === 'string' ? body.orgId : '';
119
+ return isDocumentId(orgId) ? {
120
+ orgId
121
+ } : null;
122
+ }
123
+ /**
124
+ * `automations/manage`: create, edit, switch on or off, and delete one of the
125
+ * organization's automations.
126
+ *
127
+ * A DELETE is soft — `deletedAt` stamped and the automation switched off —
128
+ * because the id is what every run history row and every waiting person
129
+ * names: the rows keep a name to show, and the people waiting inside it are
130
+ * stopped by the resume's kill switch on their next beat. Deleting and
131
+ * switching off are never refused for want of a plan, so an organization
132
+ * that downgraded can always stop what it built.
133
+ */ export function createOrgAutomationsManageHandler(overrides = {}) {
134
+ return async (req, res)=>{
135
+ var _ref, _ref1, _ref2;
136
+ var _req_body, _req_body1, _req_body2;
137
+ const deps = _extends({}, defaultDeps(), overrides);
138
+ if (req.method !== 'POST') {
139
+ return res.status(405).json({
140
+ error: 'Method not allowed'
141
+ });
142
+ }
143
+ const orgId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.orgId) != null ? _ref : '');
144
+ const action = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.action) != null ? _ref1 : '');
145
+ if (!isDocumentId(orgId)) {
146
+ return res.status(400).json({
147
+ error: 'Invalid orgId'
148
+ });
149
+ }
150
+ if (!ORG_AUTOMATION_MANAGE_ACTIONS.includes(action)) {
151
+ return res.status(400).json({
152
+ error: 'Unknown action'
153
+ });
154
+ }
155
+ const automationId = String((_ref2 = (_req_body2 = req.body) == null ? void 0 : _req_body2.automationId) != null ? _ref2 : '');
156
+ if (action !== 'create' && !isDocumentId(automationId)) {
157
+ return res.status(400).json({
158
+ error: 'Invalid automationId'
159
+ });
160
+ }
161
+ const token = bearerToken(req.headers);
162
+ if (!token) return res.status(401).json({
163
+ error: 'Unauthenticated'
164
+ });
165
+ try {
166
+ var _member_role, _caller_email;
167
+ var _req_body3;
168
+ const caller = await deps.verifyIdToken(token).catch(()=>null);
169
+ if (!caller) return res.status(401).json({
170
+ error: 'Unauthenticated'
171
+ });
172
+ const membership = await deps.resolveOrgMembership(caller.uid, orgId);
173
+ const member = membership == null ? void 0 : membership.member;
174
+ if (!member || !isOrgWideMember(member) || !ORG_MANAGE_ROLES.includes(String((_member_role = member.role) != null ? _member_role : ''))) {
175
+ return res.status(403).json({
176
+ error: 'Not an organization owner, admin or editor'
177
+ });
178
+ }
179
+ /*
180
+ * A request naming no site reaches here past only the release gate —
181
+ * the dispatcher's per-site switch needs a site — so the organization's
182
+ * own switch is asked here.
183
+ */ const org = await deps.getOrgDoc(orgId);
184
+ if (!org || !isPluginEnabled(org, BUNDLE_ID)) {
185
+ return res.status(404).json({
186
+ error: 'Not found'
187
+ });
188
+ }
189
+ const enabling = action === 'create' || action === 'update' || action === 'setEnabled' && ((_req_body3 = req.body) == null ? void 0 : _req_body3.enabled) === true;
190
+ if (enabling && !checkEntitlement(org, 'actions')) {
191
+ var _planLabelGrantingFeature;
192
+ return res.status(403).json({
193
+ error: `Org automations need the ${(_planLabelGrantingFeature = planLabelGrantingFeature('actions')) != null ? _planLabelGrantingFeature : 'Pro'} plan`
194
+ });
195
+ }
196
+ const firestore = deps.firestore();
197
+ const collection = automationsOf(firestore, orgId);
198
+ const actor = {
199
+ uid: caller.uid,
200
+ email: (_caller_email = caller.email) != null ? _caller_email : null
201
+ };
202
+ const logged = (verb, id, name)=>deps.logOrgActivity(orgId, actor, verb, {
203
+ type: ORG_AUTOMATION_ACTIVITY_TARGET,
204
+ id,
205
+ name
206
+ });
207
+ if (action === 'create' || action === 'update') {
208
+ var _req_body4;
209
+ const read = readOrgAutomation((_req_body4 = req.body) == null ? void 0 : _req_body4.automation);
210
+ if (read.ok === false) {
211
+ return res.status(400).json({
212
+ error: read.problem
213
+ });
214
+ }
215
+ const fields = read.value;
216
+ if (!await placementIsTheOrgs(firestore, orgId, fields.visibleTo)) {
217
+ return res.status(400).json({
218
+ error: 'A site you chose is not in this organization'
219
+ });
220
+ }
221
+ if (action === 'create') {
222
+ /*
223
+ * Counted before the write rather than inside a transaction. The
224
+ * cap bounds storage, not money, so the overshoot two simultaneous
225
+ * saves can buy is one document — the trade the site actions cap
226
+ * makes for the same reason.
227
+ */ const live = (await collection.where('deletedAt', '==', null).count().get()).data().count;
228
+ if (live >= ORG_AUTOMATIONS_MAX) {
229
+ return res.status(409).json({
230
+ error: `An organization holds at most ${ORG_AUTOMATIONS_MAX} org ` + 'automations — delete one you no longer use first'
231
+ });
232
+ }
233
+ const id = deps.newId();
234
+ await collection.doc(id).create(_extends({}, fields, {
235
+ pausedHostIds: [],
236
+ deletedAt: null,
237
+ createdAt: FieldValue.serverTimestamp(),
238
+ createdBy: caller.uid,
239
+ updatedAt: FieldValue.serverTimestamp(),
240
+ updatedBy: caller.uid
241
+ }));
242
+ await logged('Created an org automation', id, fields.name);
243
+ return res.status(200).json({
244
+ automationId: id
245
+ });
246
+ }
247
+ const outcome = await firestore.runTransaction(async (transaction)=>{
248
+ const ref = collection.doc(automationId);
249
+ const current = await transaction.get(ref);
250
+ if (!current.exists || current.get('deletedAt')) return 'missing';
251
+ transaction.update(ref, _extends({}, fields, {
252
+ // A site the automation no longer runs on has nothing left to
253
+ // pause: its entry goes, so a later placement starts unpaused.
254
+ pausedHostIds: prunePausedHostIds(orgAutomationPausedHostIds(current.data()), fields.visibleTo),
255
+ updatedAt: FieldValue.serverTimestamp(),
256
+ updatedBy: caller.uid
257
+ }));
258
+ return 'updated';
259
+ });
260
+ if (outcome === 'missing') {
261
+ return res.status(404).json({
262
+ error: 'Unknown org automation'
263
+ });
264
+ }
265
+ await logged('Edited an org automation', automationId, fields.name);
266
+ return res.status(200).json({
267
+ automationId
268
+ });
269
+ }
270
+ if (action === 'setEnabled') {
271
+ var _req_body5;
272
+ if (typeof ((_req_body5 = req.body) == null ? void 0 : _req_body5.enabled) !== 'boolean') {
273
+ return res.status(400).json({
274
+ error: 'Say whether it is enabled'
275
+ });
276
+ }
277
+ const enabled = req.body.enabled;
278
+ const name = await firestore.runTransaction(async (transaction)=>{
279
+ var _current_get;
280
+ const ref = collection.doc(automationId);
281
+ const current = await transaction.get(ref);
282
+ if (!current.exists || current.get('deletedAt')) return null;
283
+ transaction.update(ref, {
284
+ enabled,
285
+ updatedAt: FieldValue.serverTimestamp(),
286
+ updatedBy: caller.uid
287
+ });
288
+ return String((_current_get = current.get('name')) != null ? _current_get : '');
289
+ });
290
+ if (name === null) {
291
+ return res.status(404).json({
292
+ error: 'Unknown org automation'
293
+ });
294
+ }
295
+ await logged(enabled ? 'Switched on an org automation' : 'Switched off an org automation', automationId, name);
296
+ return res.status(200).json({
297
+ automationId,
298
+ enabled
299
+ });
300
+ }
301
+ // delete
302
+ const deleted = await firestore.runTransaction(async (transaction)=>{
303
+ var _current_get;
304
+ const ref = collection.doc(automationId);
305
+ const current = await transaction.get(ref);
306
+ if (!current.exists) return null;
307
+ // Deleting twice is the same request answered again.
308
+ if (current.get('deletedAt')) return {
309
+ name: '',
310
+ again: true
311
+ };
312
+ transaction.update(ref, {
313
+ deletedAt: FieldValue.serverTimestamp(),
314
+ deletedBy: caller.uid,
315
+ // Off as well, so the engine's query — which asks for switched-on
316
+ // automations only — never spends one of its places on it.
317
+ enabled: false,
318
+ updatedAt: FieldValue.serverTimestamp(),
319
+ updatedBy: caller.uid
320
+ });
321
+ return {
322
+ name: String((_current_get = current.get('name')) != null ? _current_get : ''),
323
+ again: false
324
+ };
325
+ });
326
+ if (!deleted) {
327
+ return res.status(404).json({
328
+ error: 'Unknown org automation'
329
+ });
330
+ }
331
+ if (!deleted.again) {
332
+ await logged('Deleted an org automation', automationId, deleted.name);
333
+ }
334
+ return res.status(200).json({
335
+ automationId,
336
+ deleted: true
337
+ });
338
+ } catch (error) {
339
+ console.error('[org automations] manage failed', orgId, action, error);
340
+ return res.status(500).json({
341
+ error: 'The request could not be completed'
342
+ });
343
+ }
344
+ };
345
+ }
346
+ /**
347
+ * `automations/pause`: pause or resume one org automation on ONE site.
348
+ *
349
+ * `pausedHostIds` is changed with `arrayUnion`/`arrayRemove` of the named
350
+ * site alone, inside a transaction that first confirms the automation runs
351
+ * there — so a site's editor can take their own site out of an org
352
+ * automation and put it back, and can never touch another site's entry or
353
+ * the automation itself. Pausing stops the people already waiting inside it
354
+ * on this site as well, on their next beat: the resume reads a pause as the
355
+ * kill switch.
356
+ *
357
+ * Never refused for want of a plan: a pause is how a site stops something,
358
+ * and stopping is always allowed.
359
+ */ export function createOrgAutomationPauseHandler(overrides = {}) {
360
+ return async (req, res)=>{
361
+ var _ref, _ref1;
362
+ var _req_body, _req_body1, _req_body2;
363
+ const deps = _extends({}, defaultDeps(), overrides);
364
+ if (req.method !== 'POST') {
365
+ return res.status(405).json({
366
+ error: 'Method not allowed'
367
+ });
368
+ }
369
+ const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
370
+ const automationId = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.automationId) != null ? _ref1 : '');
371
+ const paused = (_req_body2 = req.body) == null ? void 0 : _req_body2.paused;
372
+ if (!isDocumentId(hostId)) {
373
+ return res.status(400).json({
374
+ error: 'Invalid hostId'
375
+ });
376
+ }
377
+ if (!isDocumentId(automationId)) {
378
+ return res.status(400).json({
379
+ error: 'Invalid automationId'
380
+ });
381
+ }
382
+ if (typeof paused !== 'boolean') {
383
+ return res.status(400).json({
384
+ error: 'Say whether it is paused'
385
+ });
386
+ }
387
+ const token = bearerToken(req.headers);
388
+ if (!token) return res.status(401).json({
389
+ error: 'Unauthenticated'
390
+ });
391
+ try {
392
+ var _ref2, _caller_uid, _host_get, _caller_email;
393
+ var _req_body3;
394
+ const caller = await deps.verifyIdToken(token).catch(()=>null);
395
+ if (!caller) return res.status(401).json({
396
+ error: 'Unauthenticated'
397
+ });
398
+ const firestore = deps.firestore();
399
+ const host = await firestore.collection('hosts').doc(hostId).get();
400
+ if (!host.exists) return res.status(404).json({
401
+ error: 'Unknown site'
402
+ });
403
+ const orgId = await deps.resolveOrgIdForHost(hostId);
404
+ if (!orgId) {
405
+ return res.status(409).json({
406
+ error: 'This site is not part of an organization, so it runs no org automations.'
407
+ });
408
+ }
409
+ const requestedOrgId = String((_ref2 = (_req_body3 = req.body) == null ? void 0 : _req_body3.orgId) != null ? _ref2 : '');
410
+ if (requestedOrgId && requestedOrgId !== orgId) {
411
+ return res.status(400).json({
412
+ error: 'That site is not in this organization'
413
+ });
414
+ }
415
+ /*
416
+ * The site's own admins and editors, read off the host's `memberRoles`
417
+ * projection — the same answer every site door asks — or an org-wide
418
+ * editor, asked of the membership itself so a projection that has not
419
+ * caught up cannot refuse the organization's own managers.
420
+ */ const siteRole = String((_caller_uid = ((_host_get = host.get('memberRoles')) != null ? _host_get : {})[caller.uid]) != null ? _caller_uid : '');
421
+ let allowed = SITE_PAUSE_ROLES.includes(siteRole);
422
+ if (!allowed) {
423
+ var _ref3;
424
+ const membership = await deps.resolveOrgMembership(caller.uid, orgId);
425
+ const member = membership == null ? void 0 : membership.member;
426
+ allowed = Boolean(member) && isOrgWideMember(member) && ORG_MANAGE_ROLES.includes(String((_ref3 = member == null ? void 0 : member.role) != null ? _ref3 : ''));
427
+ }
428
+ if (!allowed) {
429
+ return res.status(403).json({
430
+ error: 'Not an admin or editor of this site'
431
+ });
432
+ }
433
+ const name = await firestore.runTransaction(async (transaction)=>{
434
+ var _current_get;
435
+ const ref = automationsOf(firestore, orgId).doc(automationId);
436
+ const current = await transaction.get(ref);
437
+ if (!current.exists || current.get('deletedAt') || !visibleToHost(current.get('visibleTo'), hostId)) {
438
+ return null;
439
+ }
440
+ transaction.update(ref, {
441
+ pausedHostIds: paused ? FieldValue.arrayUnion(hostId) : FieldValue.arrayRemove(hostId)
442
+ });
443
+ return String((_current_get = current.get('name')) != null ? _current_get : '');
444
+ });
445
+ if (name === null) {
446
+ return res.status(404).json({
447
+ error: 'No org automation by that id runs on this site'
448
+ });
449
+ }
450
+ await deps.logHostActivity(hostId, {
451
+ uid: caller.uid,
452
+ email: (_caller_email = caller.email) != null ? _caller_email : null
453
+ }, paused ? 'Paused an org automation on this site' : 'Resumed an org automation on this site', {
454
+ type: 'workflow',
455
+ id: automationId,
456
+ name
457
+ });
458
+ return res.status(200).json({
459
+ automationId,
460
+ hostId,
461
+ paused
462
+ });
463
+ } catch (error) {
464
+ console.error('[org automations] pause failed', hostId, automationId, error);
465
+ return res.status(500).json({
466
+ error: 'The request could not be completed'
467
+ });
468
+ }
469
+ };
470
+ }
471
+ export const orgAutomationsManageHandler = createOrgAutomationsManageHandler();
472
+ export const orgAutomationPauseHandler = createOrgAutomationPauseHandler();
473
+
474
+ //# sourceMappingURL=org-automations-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/server/org-automations-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WRITING AN ORG AUTOMATION, and pausing one on a site (AGL-3302).\n *\n * `orgs/{orgId}/automations` is closed to every client write, the way the\n * outreach collections are: an org automation runs on every site it is\n * placed on, as that site, so its trigger, its steps and its placement are\n * decisions the server checks — the vocabulary, the organization's sites, the\n * plan — and nothing a browser may write around.\n *\n * ## Two doors\n *\n * `automations/manage` is the organization's: it creates, edits, switches on\n * and off, and deletes. It names the org and NO site, so it declares a\n * subject resolver for the dispatchers' release gate and asks the org's\n * plugin switch itself; the caller must be an org-wide owner, admin or\n * editor.\n *\n * `automations/pause` is the SITE's — the host level control. It names the\n * site, so the dispatcher applies that site's plugin switch as for any site\n * request, and it changes one thing: whether this automation is paused on\n * THIS site. The site's own admins and editors may use it, and so may an\n * org-wide editor; nobody may pause a site through it but the one named.\n */\n\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { hostIdsFromScope, visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport {\n type AglynOrgMember,\n checkEntitlement,\n isOrgWideMember,\n type PluginApiHandler,\n type PluginApiRequestSubject,\n planLabelGrantingFeature,\n} from '@aglyn/aglyn/server'\n/*\n * The MODULES, not the barrel: `@aglyn/tenant-data-admin`'s index reaches the\n * Next render cache, and everything these doors need is the Admin SDK handle,\n * the id predicate and the org reads.\n */\nimport { isDocumentId } from '@aglyn/tenant-data-admin/server/document-id'\nimport firebaseAdmin from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport {\n getOrgDoc,\n logHostActivity,\n logOrgActivity,\n resolveOrgIdForHost,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin/server/organizations'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n ORG_AUTOMATION_ACTIVITY_TARGET,\n ORG_AUTOMATION_API_ROUTES,\n ORG_AUTOMATIONS_COLLECTION,\n ORG_AUTOMATIONS_MAX,\n orgAutomationPausedHostIds,\n prunePausedHostIds,\n readOrgAutomation,\n} from '../model/org-automations'\n\nexport { ORG_AUTOMATION_API_ROUTES }\n\n/** What `automations/manage` may be asked to do. */\nexport const ORG_AUTOMATION_MANAGE_ACTIONS = [\n 'create',\n 'update',\n 'setEnabled',\n 'delete',\n] as const\n\ntype ManageAction = (typeof ORG_AUTOMATION_MANAGE_ACTIONS)[number]\n\n/** The org roles that may write the organization's automations. */\nconst ORG_MANAGE_ROLES = ['owner', 'admin', 'editor'] as const\n\n/** The site roles that may pause an org automation on their own site. */\nconst SITE_PAUSE_ROLES = ['admin', 'editor'] as const\n\n/** What the doors reach outside this module; specs hand in their own. */\nexport interface OrgAutomationRouteDeps {\n firestore(): FirebaseFirestore.Firestore\n verifyIdToken(token: string): Promise<{ uid: string; email?: string | null }>\n resolveOrgMembership(\n uid: string,\n orgId: string,\n ): Promise<{ orgId: string; member: Partial<AglynOrgMember> } | null>\n getOrgDoc(orgId: string): Promise<Record<string, unknown> | null>\n resolveOrgIdForHost(hostId: string): Promise<string | null>\n logOrgActivity(\n orgId: string,\n actor: { uid: string; email?: string | null },\n action: string,\n target: { type: typeof ORG_AUTOMATION_ACTIVITY_TARGET; id: string; name?: string },\n ): Promise<void>\n logHostActivity(\n hostId: string,\n actor: { uid: string; email?: string | null },\n action: string,\n target: { type: 'workflow'; id: string; name?: string },\n ): Promise<void>\n newId(): string\n}\n\nfunction defaultDeps(): OrgAutomationRouteDeps {\n return {\n firestore: () =>\n firebaseAdmin.app().firestore() as unknown as FirebaseFirestore.Firestore,\n verifyIdToken: async (token) => {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(token)\n return { uid: decoded.uid, email: decoded.email ?? null }\n },\n resolveOrgMembership: async (uid, orgId) =>\n (await resolveOrgMembership(uid, orgId)) as never,\n getOrgDoc: async (orgId) =>\n ((await getOrgDoc(orgId)) as Record<string, unknown> | null) ?? null,\n resolveOrgIdForHost,\n logOrgActivity: (orgId, actor, action, target) =>\n logOrgActivity(orgId, actor, action, target),\n logHostActivity: (hostId, actor, action, target) =>\n logHostActivity(hostId, actor, action, target),\n newId: createResourceUid,\n }\n}\n\n/** The bearer token a request carries, or null. */\nfunction bearerToken(headers: Partial<Record<string, string | string[]>>): string | null {\n const authorization = String(headers['authorization'] ?? '')\n return authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length) || null\n : null\n}\n\nfunction automationsOf(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n): FirebaseFirestore.CollectionReference {\n return firestore\n .collection('orgs')\n .doc(orgId)\n .collection(ORG_AUTOMATIONS_COLLECTION)\n}\n\n/**\n * Whether every site a placement names belongs to this organization — asked\n * of `hostIndex`, the mirror `resolveOrgIdForHost` reads, in one round trip.\n * `['org']` names no site and always answers yes.\n */\nasync function placementIsTheOrgs(\n firestore: FirebaseFirestore.Firestore,\n orgId: string,\n visibleTo: readonly string[],\n): Promise<boolean> {\n const hostIds = hostIdsFromScope(visibleTo)\n if (!hostIds.length) return true\n if (!hostIds.every(isDocumentId)) return false\n const entries = await firestore.getAll(\n ...hostIds.map((hostId) => firestore.collection('hostIndex').doc(hostId)),\n )\n return entries.every((entry) => entry.exists && entry.get('orgId') === orgId)\n}\n\n/**\n * The organization a `automations/manage` request is for, read off its JSON\n * body, so the dispatchers' release gate asks about the right organization.\n * Consulted only for a request that names no site. Unverified, exactly as a\n * `hostId` is: the handler refuses anyone who is not an org-wide member.\n */\nexport async function orgAutomationsManageSubject(\n request: Request,\n): Promise<PluginApiRequestSubject | null> {\n if (request.method !== 'POST') return null\n const body = (await request.json().catch(() => null)) as {\n orgId?: unknown\n } | null\n const orgId = typeof body?.orgId === 'string' ? body.orgId : ''\n return isDocumentId(orgId) ? { orgId } : null\n}\n\n/**\n * `automations/manage`: create, edit, switch on or off, and delete one of the\n * organization's automations.\n *\n * A DELETE is soft — `deletedAt` stamped and the automation switched off —\n * because the id is what every run history row and every waiting person\n * names: the rows keep a name to show, and the people waiting inside it are\n * stopped by the resume's kill switch on their next beat. Deleting and\n * switching off are never refused for want of a plan, so an organization\n * that downgraded can always stop what it built.\n */\nexport function createOrgAutomationsManageHandler(\n overrides: Partial<OrgAutomationRouteDeps> = {},\n): PluginApiHandler {\n return async (req, res) => {\n const deps = { ...defaultDeps(), ...overrides }\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const orgId = String(req.body?.orgId ?? '')\n const action = String(req.body?.action ?? '') as ManageAction\n if (!isDocumentId(orgId)) {\n return res.status(400).json({ error: 'Invalid orgId' })\n }\n if (!(ORG_AUTOMATION_MANAGE_ACTIONS as readonly string[]).includes(action)) {\n return res.status(400).json({ error: 'Unknown action' })\n }\n const automationId = String(req.body?.automationId ?? '')\n if (action !== 'create' && !isDocumentId(automationId)) {\n return res.status(400).json({ error: 'Invalid automationId' })\n }\n const token = bearerToken(req.headers)\n if (!token) return res.status(401).json({ error: 'Unauthenticated' })\n\n try {\n const caller = await deps.verifyIdToken(token).catch(() => null)\n if (!caller) return res.status(401).json({ error: 'Unauthenticated' })\n const membership = await deps.resolveOrgMembership(caller.uid, orgId)\n const member = membership?.member\n if (\n !member ||\n !isOrgWideMember(member) ||\n !(ORG_MANAGE_ROLES as readonly string[]).includes(String(member.role ?? ''))\n ) {\n return res\n .status(403)\n .json({ error: 'Not an organization owner, admin or editor' })\n }\n /*\n * A request naming no site reaches here past only the release gate —\n * the dispatcher's per-site switch needs a site — so the organization's\n * own switch is asked here.\n */\n const org = await deps.getOrgDoc(orgId)\n if (!org || !isPluginEnabled(org as never, BUNDLE_ID)) {\n return res.status(404).json({ error: 'Not found' })\n }\n const enabling =\n action === 'create' ||\n action === 'update' ||\n (action === 'setEnabled' && req.body?.enabled === true)\n if (enabling && !checkEntitlement(org as never, 'actions')) {\n return res.status(403).json({\n error: `Org automations need the ${planLabelGrantingFeature('actions') ?? 'Pro'} plan`,\n })\n }\n\n const firestore = deps.firestore()\n const collection = automationsOf(firestore, orgId)\n const actor = { uid: caller.uid, email: caller.email ?? null }\n const logged = (verb: string, id: string, name: string) =>\n deps.logOrgActivity(orgId, actor, verb, {\n type: ORG_AUTOMATION_ACTIVITY_TARGET,\n id,\n name,\n })\n\n if (action === 'create' || action === 'update') {\n const read = readOrgAutomation(req.body?.automation)\n if (read.ok === false) {\n return res.status(400).json({ error: read.problem })\n }\n const fields = read.value\n if (!(await placementIsTheOrgs(firestore, orgId, fields.visibleTo))) {\n return res\n .status(400)\n .json({ error: 'A site you chose is not in this organization' })\n }\n if (action === 'create') {\n /*\n * Counted before the write rather than inside a transaction. The\n * cap bounds storage, not money, so the overshoot two simultaneous\n * saves can buy is one document — the trade the site actions cap\n * makes for the same reason.\n */\n const live = (\n await collection.where('deletedAt', '==', null).count().get()\n ).data().count\n if (live >= ORG_AUTOMATIONS_MAX) {\n return res.status(409).json({\n error:\n `An organization holds at most ${ORG_AUTOMATIONS_MAX} org ` +\n 'automations — delete one you no longer use first',\n })\n }\n const id = deps.newId()\n await collection.doc(id).create({\n ...fields,\n pausedHostIds: [],\n deletedAt: null,\n createdAt: FieldValue.serverTimestamp(),\n createdBy: caller.uid,\n updatedAt: FieldValue.serverTimestamp(),\n updatedBy: caller.uid,\n })\n await logged('Created an org automation', id, fields.name)\n return res.status(200).json({ automationId: id })\n }\n const outcome = await firestore.runTransaction(async (transaction) => {\n const ref = collection.doc(automationId)\n const current = await transaction.get(ref)\n if (!current.exists || current.get('deletedAt')) return 'missing'\n transaction.update(ref, {\n ...fields,\n // A site the automation no longer runs on has nothing left to\n // pause: its entry goes, so a later placement starts unpaused.\n pausedHostIds: prunePausedHostIds(\n orgAutomationPausedHostIds(current.data()),\n fields.visibleTo,\n ),\n updatedAt: FieldValue.serverTimestamp(),\n updatedBy: caller.uid,\n })\n return 'updated'\n })\n if (outcome === 'missing') {\n return res.status(404).json({ error: 'Unknown org automation' })\n }\n await logged('Edited an org automation', automationId, fields.name)\n return res.status(200).json({ automationId })\n }\n\n if (action === 'setEnabled') {\n if (typeof req.body?.enabled !== 'boolean') {\n return res.status(400).json({ error: 'Say whether it is enabled' })\n }\n const enabled = req.body.enabled as boolean\n const name = await firestore.runTransaction(async (transaction) => {\n const ref = collection.doc(automationId)\n const current = await transaction.get(ref)\n if (!current.exists || current.get('deletedAt')) return null\n transaction.update(ref, {\n enabled,\n updatedAt: FieldValue.serverTimestamp(),\n updatedBy: caller.uid,\n })\n return String(current.get('name') ?? '')\n })\n if (name === null) {\n return res.status(404).json({ error: 'Unknown org automation' })\n }\n await logged(\n enabled ? 'Switched on an org automation' : 'Switched off an org automation',\n automationId,\n name,\n )\n return res.status(200).json({ automationId, enabled })\n }\n\n // delete\n const deleted = await firestore.runTransaction(async (transaction) => {\n const ref = collection.doc(automationId)\n const current = await transaction.get(ref)\n if (!current.exists) return null\n // Deleting twice is the same request answered again.\n if (current.get('deletedAt')) return { name: '', again: true }\n transaction.update(ref, {\n deletedAt: FieldValue.serverTimestamp(),\n deletedBy: caller.uid,\n // Off as well, so the engine's query — which asks for switched-on\n // automations only — never spends one of its places on it.\n enabled: false,\n updatedAt: FieldValue.serverTimestamp(),\n updatedBy: caller.uid,\n })\n return { name: String(current.get('name') ?? ''), again: false }\n })\n if (!deleted) {\n return res.status(404).json({ error: 'Unknown org automation' })\n }\n if (!deleted.again) {\n await logged('Deleted an org automation', automationId, deleted.name)\n }\n return res.status(200).json({ automationId, deleted: true })\n } catch (error) {\n console.error('[org automations] manage failed', orgId, action, error)\n return res\n .status(500)\n .json({ error: 'The request could not be completed' })\n }\n }\n}\n\n/**\n * `automations/pause`: pause or resume one org automation on ONE site.\n *\n * `pausedHostIds` is changed with `arrayUnion`/`arrayRemove` of the named\n * site alone, inside a transaction that first confirms the automation runs\n * there — so a site's editor can take their own site out of an org\n * automation and put it back, and can never touch another site's entry or\n * the automation itself. Pausing stops the people already waiting inside it\n * on this site as well, on their next beat: the resume reads a pause as the\n * kill switch.\n *\n * Never refused for want of a plan: a pause is how a site stops something,\n * and stopping is always allowed.\n */\nexport function createOrgAutomationPauseHandler(\n overrides: Partial<OrgAutomationRouteDeps> = {},\n): PluginApiHandler {\n return async (req, res) => {\n const deps = { ...defaultDeps(), ...overrides }\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const hostId = String(req.body?.hostId ?? '')\n const automationId = String(req.body?.automationId ?? '')\n const paused = req.body?.paused\n if (!isDocumentId(hostId)) {\n return res.status(400).json({ error: 'Invalid hostId' })\n }\n if (!isDocumentId(automationId)) {\n return res.status(400).json({ error: 'Invalid automationId' })\n }\n if (typeof paused !== 'boolean') {\n return res.status(400).json({ error: 'Say whether it is paused' })\n }\n const token = bearerToken(req.headers)\n if (!token) return res.status(401).json({ error: 'Unauthenticated' })\n\n try {\n const caller = await deps.verifyIdToken(token).catch(() => null)\n if (!caller) return res.status(401).json({ error: 'Unauthenticated' })\n const firestore = deps.firestore()\n const host = await firestore.collection('hosts').doc(hostId).get()\n if (!host.exists) return res.status(404).json({ error: 'Unknown site' })\n const orgId = await deps.resolveOrgIdForHost(hostId)\n if (!orgId) {\n return res.status(409).json({\n error: 'This site is not part of an organization, so it runs no org automations.',\n })\n }\n const requestedOrgId = String(req.body?.orgId ?? '')\n if (requestedOrgId && requestedOrgId !== orgId) {\n return res.status(400).json({ error: 'That site is not in this organization' })\n }\n /*\n * The site's own admins and editors, read off the host's `memberRoles`\n * projection — the same answer every site door asks — or an org-wide\n * editor, asked of the membership itself so a projection that has not\n * caught up cannot refuse the organization's own managers.\n */\n const siteRole = String((host.get('memberRoles') ?? {})[caller.uid] ?? '')\n let allowed = (SITE_PAUSE_ROLES as readonly string[]).includes(siteRole)\n if (!allowed) {\n const membership = await deps.resolveOrgMembership(caller.uid, orgId)\n const member = membership?.member\n allowed =\n Boolean(member) &&\n isOrgWideMember(member) &&\n (ORG_MANAGE_ROLES as readonly string[]).includes(String(member?.role ?? ''))\n }\n if (!allowed) {\n return res\n .status(403)\n .json({ error: 'Not an admin or editor of this site' })\n }\n\n const name = await firestore.runTransaction(async (transaction) => {\n const ref = automationsOf(firestore, orgId).doc(automationId)\n const current = await transaction.get(ref)\n if (\n !current.exists ||\n current.get('deletedAt') ||\n !visibleToHost(current.get('visibleTo') as string[] | undefined, hostId)\n ) {\n return null\n }\n transaction.update(ref, {\n pausedHostIds: paused\n ? FieldValue.arrayUnion(hostId)\n : FieldValue.arrayRemove(hostId),\n })\n return String(current.get('name') ?? '')\n })\n if (name === null) {\n return res\n .status(404)\n .json({ error: 'No org automation by that id runs on this site' })\n }\n await deps.logHostActivity(\n hostId,\n { uid: caller.uid, email: caller.email ?? null },\n paused\n ? 'Paused an org automation on this site'\n : 'Resumed an org automation on this site',\n { type: 'workflow', id: automationId, name },\n )\n return res.status(200).json({ automationId, hostId, paused })\n } catch (error) {\n console.error('[org automations] pause failed', hostId, automationId, error)\n return res\n .status(500)\n .json({ error: 'The request could not be completed' })\n }\n }\n}\n\nexport const orgAutomationsManageHandler = createOrgAutomationsManageHandler()\nexport const orgAutomationPauseHandler = createOrgAutomationPauseHandler()\n"],"names":["createResourceUid","hostIdsFromScope","visibleToHost","isPluginEnabled","checkEntitlement","isOrgWideMember","planLabelGrantingFeature","isDocumentId","firebaseAdmin","getOrgDoc","logHostActivity","logOrgActivity","resolveOrgIdForHost","resolveOrgMembership","FieldValue","BUNDLE_ID","ORG_AUTOMATION_ACTIVITY_TARGET","ORG_AUTOMATION_API_ROUTES","ORG_AUTOMATIONS_COLLECTION","ORG_AUTOMATIONS_MAX","orgAutomationPausedHostIds","prunePausedHostIds","readOrgAutomation","ORG_AUTOMATION_MANAGE_ACTIONS","ORG_MANAGE_ROLES","SITE_PAUSE_ROLES","defaultDeps","firestore","app","verifyIdToken","token","decoded","auth","uid","email","orgId","actor","action","target","hostId","newId","bearerToken","headers","authorization","String","startsWith","slice","length","automationsOf","collection","doc","placementIsTheOrgs","visibleTo","hostIds","every","entries","getAll","map","entry","exists","get","orgAutomationsManageSubject","request","method","body","json","catch","createOrgAutomationsManageHandler","overrides","req","res","deps","status","error","includes","automationId","member","caller","membership","role","org","enabling","enabled","logged","verb","id","name","type","read","automation","ok","problem","fields","value","live","where","count","data","create","pausedHostIds","deletedAt","createdAt","serverTimestamp","createdBy","updatedAt","updatedBy","outcome","runTransaction","transaction","ref","current","update","deleted","again","deletedBy","console","createOrgAutomationPauseHandler","paused","host","requestedOrgId","siteRole","allowed","Boolean","arrayUnion","arrayRemove","orgAutomationsManageHandler","orgAutomationPauseHandler"],"mappings":"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"}
@@ -22,6 +22,7 @@
22
22
  * does: the automation draft writer another plugin reaches through the core's
23
23
  * resource-drafts seam, which the AI jobs that write through it — inline at a
24
24
  * door and on the beat — find registered in the console process alone
25
- * (AGL-3026).
25
+ * (AGL-3026); and the doors that write the organization's automations
26
+ * (AGL-3302).
26
27
  */
27
28
  export declare function registerWorkflowsConsoleApi(): void;
@@ -13,8 +13,10 @@
13
13
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
- */ import { registerWorkflowsServerDeclarations } from "./declarations.server.js";
16
+ */ import { registerPluginApiRoute } from "@aglyn/aglyn/server";
17
+ import { registerWorkflowsServerDeclarations } from "./declarations.server.js";
17
18
  import { registerAutomationDraftWriter } from "./server-automation-drafts.js";
19
+ import { ORG_AUTOMATION_API_ROUTES, orgAutomationPauseHandler, orgAutomationsManageHandler, orgAutomationsManageSubject } from "./server/org-automations-routes.js";
18
20
  /**
19
21
  * The workflows plugin's console server surface (AGL-2919).
20
22
  *
@@ -23,13 +25,21 @@ import { registerAutomationDraftWriter } from "./server-automation-drafts.js";
23
25
  * does: the automation draft writer another plugin reaches through the core's
24
26
  * resource-drafts seam, which the AI jobs that write through it — inline at a
25
27
  * door and on the beat — find registered in the console process alone
26
- * (AGL-3026).
28
+ * (AGL-3026); and the doors that write the organization's automations
29
+ * (AGL-3302).
27
30
  */ export function registerWorkflowsConsoleApi() {
28
31
  // The console raises host events too — a CRM stage moved, a deal won, a
29
32
  // task completed — so the engine's listener is registered here as well as
30
33
  // at boot (see `declarations.server.ts`).
31
34
  registerWorkflowsServerDeclarations();
32
35
  registerAutomationDraftWriter();
36
+ // The organization hub names its org and no site, so the route names that
37
+ // org as its subject for the release gate.
38
+ registerPluginApiRoute(ORG_AUTOMATION_API_ROUTES.manage, orgAutomationsManageHandler, {
39
+ subject: orgAutomationsManageSubject
40
+ });
41
+ // A site's own pause names the site, and is gated by that site's switch.
42
+ registerPluginApiRoute(ORG_AUTOMATION_API_ROUTES.pause, orgAutomationPauseHandler);
33
43
  }
34
44
 
35
45
  //# sourceMappingURL=server-console.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/server-console.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { registerWorkflowsServerDeclarations } from './declarations.server'\nimport { registerAutomationDraftWriter } from './server-automation-drafts'\n\n/**\n * The workflows plugin's console server surface (AGL-2919).\n *\n * The tenant surface (`registerWorkflowsApi`) serves the inbound webhook a\n * visitor-facing integration calls. This one serves what only the console\n * does: the automation draft writer another plugin reaches through the core's\n * resource-drafts seam, which the AI jobs that write through it — inline at a\n * door and on the beat — find registered in the console process alone\n * (AGL-3026).\n */\nexport function registerWorkflowsConsoleApi(): void {\n // The console raises host events too — a CRM stage moved, a deal won, a\n // task completed — so the engine's listener is registered here as well as\n // at boot (see `declarations.server.ts`).\n registerWorkflowsServerDeclarations()\n registerAutomationDraftWriter()\n}\n"],"names":["registerWorkflowsServerDeclarations","registerAutomationDraftWriter","registerWorkflowsConsoleApi"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,mCAAmC,QAAQ,2BAAuB;AAC3E,SAASC,6BAA6B,QAAQ,gCAA4B;AAE1E;;;;;;;;;CASC,GACD,OAAO,SAASC;IACd,wEAAwE;IACxE,0EAA0E;IAC1E,0CAA0C;IAC1CF;IACAC;AACF"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/server-console.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { registerWorkflowsServerDeclarations } from './declarations.server'\nimport { registerAutomationDraftWriter } from './server-automation-drafts'\nimport {\n ORG_AUTOMATION_API_ROUTES,\n orgAutomationPauseHandler,\n orgAutomationsManageHandler,\n orgAutomationsManageSubject,\n} from './server/org-automations-routes'\n\n/**\n * The workflows plugin's console server surface (AGL-2919).\n *\n * The tenant surface (`registerWorkflowsApi`) serves the inbound webhook a\n * visitor-facing integration calls. This one serves what only the console\n * does: the automation draft writer another plugin reaches through the core's\n * resource-drafts seam, which the AI jobs that write through it — inline at a\n * door and on the beat — find registered in the console process alone\n * (AGL-3026); and the doors that write the organization's automations\n * (AGL-3302).\n */\nexport function registerWorkflowsConsoleApi(): void {\n // The console raises host events too — a CRM stage moved, a deal won, a\n // task completed — so the engine's listener is registered here as well as\n // at boot (see `declarations.server.ts`).\n registerWorkflowsServerDeclarations()\n registerAutomationDraftWriter()\n // The organization hub names its org and no site, so the route names that\n // org as its subject for the release gate.\n registerPluginApiRoute(\n ORG_AUTOMATION_API_ROUTES.manage,\n orgAutomationsManageHandler,\n { subject: orgAutomationsManageSubject },\n )\n // A site's own pause names the site, and is gated by that site's switch.\n registerPluginApiRoute(ORG_AUTOMATION_API_ROUTES.pause, orgAutomationPauseHandler)\n}\n"],"names":["registerPluginApiRoute","registerWorkflowsServerDeclarations","registerAutomationDraftWriter","ORG_AUTOMATION_API_ROUTES","orgAutomationPauseHandler","orgAutomationsManageHandler","orgAutomationsManageSubject","registerWorkflowsConsoleApi","manage","subject","pause"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,mCAAmC,QAAQ,2BAAuB;AAC3E,SAASC,6BAA6B,QAAQ,gCAA4B;AAC1E,SACEC,yBAAyB,EACzBC,yBAAyB,EACzBC,2BAA2B,EAC3BC,2BAA2B,QACtB,qCAAiC;AAExC;;;;;;;;;;CAUC,GACD,OAAO,SAASC;IACd,wEAAwE;IACxE,0EAA0E;IAC1E,0CAA0C;IAC1CN;IACAC;IACA,0EAA0E;IAC1E,2CAA2C;IAC3CF,uBACEG,0BAA0BK,MAAM,EAChCH,6BACA;QAAEI,SAASH;IAA4B;IAEzC,yEAAyE;IACzEN,uBAAuBG,0BAA0BO,KAAK,EAAEN;AAC1D"}