@aglyn/plugins-workflows 1.0.0-beta.220 → 1.0.0-beta.222

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 (92) hide show
  1. package/README.md +22 -14
  2. package/package.json +12 -12
  3. package/src/lib/components/automation-step-fields.component.d.ts +1 -15
  4. package/src/lib/components/automation-step-fields.component.js +93 -10
  5. package/src/lib/components/automation-step-fields.component.js.map +1 -1
  6. package/src/lib/components/automation-trigger-conditions.component.d.ts +2 -1
  7. package/src/lib/components/automation-trigger-conditions.component.js.map +1 -1
  8. package/src/lib/components/host-actions-card.component.js +85 -77
  9. package/src/lib/components/host-actions-card.component.js.map +1 -1
  10. package/src/lib/components/host-webhooks-card.component.js +2 -1
  11. package/src/lib/components/host-webhooks-card.component.js.map +1 -1
  12. package/src/lib/components/host-workflows-card.component.js.map +1 -1
  13. package/src/lib/components/org-automation-editor.component.d.ts +2 -1
  14. package/src/lib/components/org-automation-editor.component.js.map +1 -1
  15. package/src/lib/components/org-automations-card.component.js +2 -1
  16. package/src/lib/components/org-automations-card.component.js.map +1 -1
  17. package/src/lib/components/run-quota-line.component.d.ts +17 -4
  18. package/src/lib/components/run-quota-line.component.js +16 -7
  19. package/src/lib/components/run-quota-line.component.js.map +1 -1
  20. package/src/lib/components/site-org-automations-panel.component.js +2 -1
  21. package/src/lib/components/site-org-automations-panel.component.js.map +1 -1
  22. package/src/lib/components/workflows-console-page.js +18 -7
  23. package/src/lib/components/workflows-console-page.js.map +1 -1
  24. package/src/lib/declarations.d.ts +28 -0
  25. package/src/lib/declarations.js +37 -0
  26. package/src/lib/declarations.js.map +1 -0
  27. package/src/lib/declarations.server.js +17 -0
  28. package/src/lib/declarations.server.js.map +1 -1
  29. package/src/lib/engine/run-event-actions.d.ts +5 -10
  30. package/src/lib/engine/run-event-actions.js +147 -330
  31. package/src/lib/engine/run-event-actions.js.map +1 -1
  32. package/src/lib/engine/run-event-automations.d.ts +1 -1
  33. package/src/lib/engine/run-event-automations.js.map +1 -1
  34. package/src/lib/engine/run-event-workflows.d.ts +2 -1
  35. package/src/lib/engine/run-event-workflows.js +27 -22
  36. package/src/lib/engine/run-event-workflows.js.map +1 -1
  37. package/src/lib/engine/run-meter.d.ts +98 -0
  38. package/src/lib/engine/run-meter.js +121 -0
  39. package/src/lib/engine/run-meter.js.map +1 -0
  40. package/src/lib/engine/run-org-automations.d.ts +3 -2
  41. package/src/lib/engine/run-org-automations.js +14 -11
  42. package/src/lib/engine/run-org-automations.js.map +1 -1
  43. package/src/lib/engine/workflow-steps.d.ts +1 -1
  44. package/src/lib/engine/workflow-steps.js +3 -2
  45. package/src/lib/engine/workflow-steps.js.map +1 -1
  46. package/src/lib/model/host-actions.d.ts +231 -0
  47. package/src/lib/model/host-actions.js +268 -0
  48. package/src/lib/model/host-actions.js.map +1 -0
  49. package/src/lib/model/org-automations.d.ts +2 -1
  50. package/src/lib/model/org-automations.js +6 -3
  51. package/src/lib/model/org-automations.js.map +1 -1
  52. package/src/lib/model/step-outcomes.d.ts +2 -2
  53. package/src/lib/model/step-outcomes.js +1 -1
  54. package/src/lib/model/step-outcomes.js.map +1 -1
  55. package/src/lib/plugin.js +10 -0
  56. package/src/lib/plugin.js.map +1 -1
  57. package/src/lib/server/action-test-run-route.d.ts +3 -2
  58. package/src/lib/server/action-test-run-route.js +3 -2
  59. package/src/lib/server/action-test-run-route.js.map +1 -1
  60. package/src/lib/server/org-automations-routes.d.ts +42 -1
  61. package/src/lib/server/org-automations-routes.js +120 -72
  62. package/src/lib/server/org-automations-routes.js.map +1 -1
  63. package/src/lib/server-automation-drafts.d.ts +2 -2
  64. package/src/lib/server-automation-drafts.js +9 -6
  65. package/src/lib/server-automation-drafts.js.map +1 -1
  66. package/src/lib/server.js +19 -13
  67. package/src/lib/server.js.map +1 -1
  68. package/src/lib/testing/stand-in-crm-steps.d.ts +45 -0
  69. package/src/lib/testing/stand-in-crm-steps.js +97 -0
  70. package/src/lib/testing/stand-in-crm-steps.js.map +1 -0
  71. package/src/lib/testing/stand-in-dataset-list.js +11 -3
  72. package/src/lib/testing/stand-in-dataset-list.js.map +1 -1
  73. package/src/lib/testing/stand-in-dataset-steps.d.ts +43 -0
  74. package/src/lib/testing/stand-in-dataset-steps.js +65 -0
  75. package/src/lib/testing/stand-in-dataset-steps.js.map +1 -0
  76. package/src/lib/testing/stand-in-person-records.d.ts +35 -0
  77. package/src/lib/testing/stand-in-person-records.js +54 -0
  78. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  79. package/src/lib/testing/stand-in-recipes.d.ts +35 -0
  80. package/src/lib/testing/stand-in-recipes.js +201 -0
  81. package/src/lib/testing/stand-in-recipes.js.map +1 -0
  82. package/src/lib/transfer/org-automations-package.d.ts +54 -0
  83. package/src/lib/transfer/org-automations-package.js +167 -0
  84. package/src/lib/transfer/org-automations-package.js.map +1 -0
  85. package/src/lib/transfer/org-automations-package.server.d.ts +38 -0
  86. package/src/lib/transfer/org-automations-package.server.js +296 -0
  87. package/src/lib/transfer/org-automations-package.server.js.map +1 -0
  88. package/src/lib/usage-axes.js +5 -1
  89. package/src/lib/usage-axes.js.map +1 -1
  90. package/src/lib/engine/crm-action-steps.d.ts +0 -142
  91. package/src/lib/engine/crm-action-steps.js +0 -452
  92. package/src/lib/engine/crm-action-steps.js.map +0 -1
@@ -1,5 +1,12 @@
1
1
  export interface RunQuotaLineProps {
2
- hostId: string;
2
+ /**
3
+ * The site the line sits on, or `null` on the organization's hub. Its own
4
+ * counter is read only when there is no workspace id to read the
5
+ * workspace's by.
6
+ */
7
+ hostId: string | null;
8
+ /** The workspace whose runs are reported; defaults to the org doc's `$id`. */
9
+ orgId?: string | null;
3
10
  /** The resolved org doc, from the plugin shell. */
4
11
  org: unknown;
5
12
  /** Which counter to report. */
@@ -16,9 +23,15 @@ export interface RunQuotaLineProps {
16
23
  * returns early once `used + actions.length > limit`) was invisible on the
17
24
  * page where a person is looking at their automations.
18
25
  *
19
- * Renders NOTHING while the counter or the plan is unresolved. `0 runs
20
- * this month` is what an unread counter looks like, and it is the one
21
- * reading that makes a customer stop debugging.
26
+ * The figure is the WORKSPACE's (AGL-3472): the band is the organization's,
27
+ * and the run gates hold it to every site's runs, counted at
28
+ * `orgs/{orgId}/counters/{counter}`. One site's own count against the whole
29
+ * band would read as headroom the gate has already spent elsewhere. Only a
30
+ * line with no workspace id falls back to the site's counter.
31
+ *
32
+ * Renders NOTHING while the counter or the plan is unresolved, or when the
33
+ * counter cannot be read. `0 runs this month` is what an unread counter looks
34
+ * like, and it is the one reading that makes a customer stop debugging.
22
35
  */
23
36
  export declare function RunQuotaLine(props: RunQuotaLineProps): import("react").JSX.Element;
24
37
  export declare namespace RunQuotaLine {
@@ -38,23 +38,32 @@ const COUNTER_LIMIT = {
38
38
  * returns early once `used + actions.length > limit`) was invisible on the
39
39
  * page where a person is looking at their automations.
40
40
  *
41
- * Renders NOTHING while the counter or the plan is unresolved. `0 runs
42
- * this month` is what an unread counter looks like, and it is the one
43
- * reading that makes a customer stop debugging.
41
+ * The figure is the WORKSPACE's (AGL-3472): the band is the organization's,
42
+ * and the run gates hold it to every site's runs, counted at
43
+ * `orgs/{orgId}/counters/{counter}`. One site's own count against the whole
44
+ * band would read as headroom the gate has already spent elsewhere. Only a
45
+ * line with no workspace id falls back to the site's counter.
46
+ *
47
+ * Renders NOTHING while the counter or the plan is unresolved, or when the
48
+ * counter cannot be read. `0 runs this month` is what an unread counter looks
49
+ * like, and it is the one reading that makes a customer stop debugging.
44
50
  */ export function RunQuotaLine(props) {
45
- var _ref;
51
+ var _props_orgId, _ref;
46
52
  var _resolveOrgEntitlements;
47
53
  const { hostId, org, counter } = props;
48
54
  const firestore = useFirestore();
49
55
  const monthKey = new Date().toISOString().slice(0, 7);
50
- const { data: counterDoc, status } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId, 'counters', counter), [
56
+ const orgId = (_props_orgId = props.orgId) != null ? _props_orgId : org == null ? void 0 : org.$id;
57
+ const workspaceId = typeof orgId === 'string' && orgId ? orgId : null;
58
+ const { data: counterDoc, status } = useFirestoreDoc(()=>workspaceId ? doc(firestore, 'orgs', workspaceId, 'counters', counter) : hostId ? doc(firestore, 'hosts', hostId, 'counters', counter) : null, [
51
59
  firestore,
60
+ workspaceId,
52
61
  hostId,
53
62
  counter
54
63
  ]);
55
- // A host that has never run one has no counter document; that is a
64
+ // A workspace that has never run one has no counter document; that is a
56
65
  // settled zero, not a pending read.
57
- if (status === 'loading') return null;
66
+ if (status !== 'success') return null;
58
67
  const limit = (_resolveOrgEntitlements = resolveOrgEntitlements(org)) == null ? void 0 : _resolveOrgEntitlements[COUNTER_LIMIT[counter]];
59
68
  if (typeof limit !== 'number') return null;
60
69
  const used = Number((_ref = counterDoc == null ? void 0 : counterDoc[monthKey]) != null ? _ref : 0);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/run-quota-line.component.tsx"],"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'use client'\n\nimport { isUnlimitedQuota, resolveOrgEntitlements } from '@aglyn/aglyn'\nimport { Typography } from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport {\n useFirestore,\n useFirestoreDoc,\n} from '@aglyn/tenant-feature-instance'\n\nexport interface RunQuotaLineProps {\n hostId: string\n /** The resolved org doc, from the plugin shell. */\n org: unknown\n /** Which counter to report. */\n counter: 'workflowRuns' | 'actionRuns'\n}\n\nconst COUNTER_LABEL = {\n workflowRuns: 'workflow runs',\n actionRuns: 'action runs',\n} as const\n\nconst COUNTER_LIMIT = {\n workflowRuns: 'workflowRunsPerMonth',\n actionRuns: 'actionRunsPerMonth',\n} as const\n\n/**\n * `1,284 runs this month · 500,000 included` (AGL-2171).\n *\n * `/product/workflows`'s run-history mockup puts this line opposite the\n * heading. The only place the product reported it was the Billing page, as\n * `1284 / 500000` — and `actionRunsPerMonth` had **no customer-facing\n * surface at all**, only the staff panel and the usage-alerts route. So\n * the quota that silently stops an automation from running (`runEventActions`\n * returns early once `used + actions.length > limit`) was invisible on the\n * page where a person is looking at their automations.\n *\n * Renders NOTHING while the counter or the plan is unresolved. `0 runs\n * this month` is what an unread counter looks like, and it is the one\n * reading that makes a customer stop debugging.\n */\nexport function RunQuotaLine(props: RunQuotaLineProps) {\n const { hostId, org, counter } = props\n const firestore = useFirestore()\n const monthKey = new Date().toISOString().slice(0, 7)\n const { data: counterDoc, status } = useFirestoreDoc<Record<string, unknown>>(\n () => doc(firestore, 'hosts', hostId, 'counters', counter),\n [firestore, hostId, counter],\n )\n // A host that has never run one has no counter document; that is a\n // settled zero, not a pending read.\n if (status === 'loading') return null\n const limit = (\n resolveOrgEntitlements(org as never) as Record<string, unknown>\n )?.[COUNTER_LIMIT[counter]]\n if (typeof limit !== 'number') return null\n const used = Number(counterDoc?.[monthKey] ?? 0)\n // An unlimited band (an uncapped staff comp, AGL-3049) has no figure to\n // include: `Infinity.toLocaleString()` would print \"∞ included\".\n return (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${used.toLocaleString()} ${COUNTER_LABEL[counter]} this month · ` +\n (isUnlimitedQuota(limit)\n ? 'no monthly limit'\n : `${limit.toLocaleString()} included`)}\n </Typography>\n )\n}\nRunQuotaLine.displayName = 'RunQuotaLine'\n\nexport default RunQuotaLine\n"],"names":["isUnlimitedQuota","resolveOrgEntitlements","Typography","doc","useFirestore","useFirestoreDoc","COUNTER_LABEL","workflowRuns","actionRuns","COUNTER_LIMIT","RunQuotaLine","props","hostId","org","counter","firestore","monthKey","Date","toISOString","slice","data","counterDoc","status","limit","used","Number","variant","color","toLocaleString","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,gBAAgB,EAAEC,sBAAsB,QAAQ,eAAc;AACvE,SAASC,UAAU,QAAQ,gBAAe;AAC1C,SAASC,GAAG,QAAQ,qBAAoB;AACxC,SACEC,YAAY,EACZC,eAAe,QACV,iCAAgC;AAUvC,MAAMC,gBAAgB;IACpBC,cAAc;IACdC,YAAY;AACd;AAEA,MAAMC,gBAAgB;IACpBF,cAAc;IACdC,YAAY;AACd;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASE,aAAaC,KAAwB;;QAYjDV;IAXF,MAAM,EAAEW,MAAM,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGH;IACjC,MAAMI,YAAYX;IAClB,MAAMY,WAAW,IAAIC,OAAOC,WAAW,GAAGC,KAAK,CAAC,GAAG;IACnD,MAAM,EAAEC,MAAMC,UAAU,EAAEC,MAAM,EAAE,GAAGjB,gBACnC,IAAMF,IAAIY,WAAW,SAASH,QAAQ,YAAYE,UAClD;QAACC;QAAWH;QAAQE;KAAQ;IAE9B,mEAAmE;IACnE,oCAAoC;IACpC,IAAIQ,WAAW,WAAW,OAAO;IACjC,MAAMC,SACJtB,0BAAAA,uBAAuBY,yBADX,AACZZ,uBACC,CAACQ,aAAa,CAACK,QAAQ,CAAC;IAC3B,IAAI,OAAOS,UAAU,UAAU,OAAO;IACtC,MAAMC,OAAOC,eAAOJ,8BAAAA,UAAY,CAACL,SAAS,mBAAI;IAC9C,wEAAwE;IACxE,iEAAiE;IACjE,qBACE,KAACd;QAAWwB,SAAQ;QAAUC,OAAM;kBACjC,GAAGH,KAAKI,cAAc,GAAG,CAAC,EAAEtB,aAAa,CAACQ,QAAQ,CAAC,cAAc,CAAC,GAChEd,CAAAA,iBAAiBuB,SACd,qBACA,GAAGA,MAAMK,cAAc,GAAG,SAAS,CAAC,AAAD;;AAG/C;AACAlB,aAAamB,WAAW,GAAG;AAE3B,eAAenB,aAAY"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/run-quota-line.component.tsx"],"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'use client'\n\nimport { isUnlimitedQuota, resolveOrgEntitlements } from '@aglyn/aglyn'\nimport { Typography } from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport {\n useFirestore,\n useFirestoreDoc,\n} from '@aglyn/tenant-feature-instance'\n\nexport interface RunQuotaLineProps {\n /**\n * The site the line sits on, or `null` on the organization's hub. Its own\n * counter is read only when there is no workspace id to read the\n * workspace's by.\n */\n hostId: string | null\n /** The workspace whose runs are reported; defaults to the org doc's `$id`. */\n orgId?: string | null\n /** The resolved org doc, from the plugin shell. */\n org: unknown\n /** Which counter to report. */\n counter: 'workflowRuns' | 'actionRuns'\n}\n\nconst COUNTER_LABEL = {\n workflowRuns: 'workflow runs',\n actionRuns: 'action runs',\n} as const\n\nconst COUNTER_LIMIT = {\n workflowRuns: 'workflowRunsPerMonth',\n actionRuns: 'actionRunsPerMonth',\n} as const\n\n/**\n * `1,284 runs this month · 500,000 included` (AGL-2171).\n *\n * `/product/workflows`'s run-history mockup puts this line opposite the\n * heading. The only place the product reported it was the Billing page, as\n * `1284 / 500000` — and `actionRunsPerMonth` had **no customer-facing\n * surface at all**, only the staff panel and the usage-alerts route. So\n * the quota that silently stops an automation from running (`runEventActions`\n * returns early once `used + actions.length > limit`) was invisible on the\n * page where a person is looking at their automations.\n *\n * The figure is the WORKSPACE's (AGL-3472): the band is the organization's,\n * and the run gates hold it to every site's runs, counted at\n * `orgs/{orgId}/counters/{counter}`. One site's own count against the whole\n * band would read as headroom the gate has already spent elsewhere. Only a\n * line with no workspace id falls back to the site's counter.\n *\n * Renders NOTHING while the counter or the plan is unresolved, or when the\n * counter cannot be read. `0 runs this month` is what an unread counter looks\n * like, and it is the one reading that makes a customer stop debugging.\n */\nexport function RunQuotaLine(props: RunQuotaLineProps) {\n const { hostId, org, counter } = props\n const firestore = useFirestore()\n const monthKey = new Date().toISOString().slice(0, 7)\n const orgId =\n props.orgId ?? (org as { $id?: unknown } | null | undefined)?.$id\n const workspaceId = typeof orgId === 'string' && orgId ? orgId : null\n const { data: counterDoc, status } = useFirestoreDoc<Record<string, unknown>>(\n () =>\n workspaceId\n ? doc(firestore, 'orgs', workspaceId, 'counters', counter)\n : hostId\n ? doc(firestore, 'hosts', hostId, 'counters', counter)\n : null,\n [firestore, workspaceId, hostId, counter],\n )\n // A workspace that has never run one has no counter document; that is a\n // settled zero, not a pending read.\n if (status !== 'success') return null\n const limit = (\n resolveOrgEntitlements(org as never) as Record<string, unknown>\n )?.[COUNTER_LIMIT[counter]]\n if (typeof limit !== 'number') return null\n const used = Number(counterDoc?.[monthKey] ?? 0)\n // An unlimited band (an uncapped staff comp, AGL-3049) has no figure to\n // include: `Infinity.toLocaleString()` would print \"∞ included\".\n return (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${used.toLocaleString()} ${COUNTER_LABEL[counter]} this month · ` +\n (isUnlimitedQuota(limit)\n ? 'no monthly limit'\n : `${limit.toLocaleString()} included`)}\n </Typography>\n )\n}\nRunQuotaLine.displayName = 'RunQuotaLine'\n\nexport default RunQuotaLine\n"],"names":["isUnlimitedQuota","resolveOrgEntitlements","Typography","doc","useFirestore","useFirestoreDoc","COUNTER_LABEL","workflowRuns","actionRuns","COUNTER_LIMIT","RunQuotaLine","props","hostId","org","counter","firestore","monthKey","Date","toISOString","slice","orgId","$id","workspaceId","data","counterDoc","status","limit","used","Number","variant","color","toLocaleString","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,gBAAgB,EAAEC,sBAAsB,QAAQ,eAAc;AACvE,SAASC,UAAU,QAAQ,gBAAe;AAC1C,SAASC,GAAG,QAAQ,qBAAoB;AACxC,SACEC,YAAY,EACZC,eAAe,QACV,iCAAgC;AAiBvC,MAAMC,gBAAgB;IACpBC,cAAc;IACdC,YAAY;AACd;AAEA,MAAMC,gBAAgB;IACpBF,cAAc;IACdC,YAAY;AACd;AAEA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASE,aAAaC,KAAwB;QAKjDA;QAeAV;IAnBF,MAAM,EAAEW,MAAM,EAAEC,GAAG,EAAEC,OAAO,EAAE,GAAGH;IACjC,MAAMI,YAAYX;IAClB,MAAMY,WAAW,IAAIC,OAAOC,WAAW,GAAGC,KAAK,CAAC,GAAG;IACnD,MAAMC,SACJT,eAAAA,MAAMS,KAAK,YAAXT,eAAgBE,uBAAD,AAACA,IAA8CQ,GAAG;IACnE,MAAMC,cAAc,OAAOF,UAAU,YAAYA,QAAQA,QAAQ;IACjE,MAAM,EAAEG,MAAMC,UAAU,EAAEC,MAAM,EAAE,GAAGpB,gBACnC,IACEiB,cACInB,IAAIY,WAAW,QAAQO,aAAa,YAAYR,WAChDF,SACET,IAAIY,WAAW,SAASH,QAAQ,YAAYE,WAC5C,MACR;QAACC;QAAWO;QAAaV;QAAQE;KAAQ;IAE3C,wEAAwE;IACxE,oCAAoC;IACpC,IAAIW,WAAW,WAAW,OAAO;IACjC,MAAMC,SACJzB,0BAAAA,uBAAuBY,yBADX,AACZZ,uBACC,CAACQ,aAAa,CAACK,QAAQ,CAAC;IAC3B,IAAI,OAAOY,UAAU,UAAU,OAAO;IACtC,MAAMC,OAAOC,eAAOJ,8BAAAA,UAAY,CAACR,SAAS,mBAAI;IAC9C,wEAAwE;IACxE,iEAAiE;IACjE,qBACE,KAACd;QAAW2B,SAAQ;QAAUC,OAAM;kBACjC,GAAGH,KAAKI,cAAc,GAAG,CAAC,EAAEzB,aAAa,CAACQ,QAAQ,CAAC,cAAc,CAAC,GAChEd,CAAAA,iBAAiB0B,SACd,qBACA,GAAGA,MAAMK,cAAc,GAAG,SAAS,CAAC,AAAD;;AAG/C;AACArB,aAAasB,WAAW,GAAG;AAE3B,eAAetB,aAAY"}
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
17
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
- import { HOST_ACTION_STEP_LABELS, hostEventLabel, pluginDocsHelp, scopeTokensForHost } from "@aglyn/aglyn";
18
+ import { hostEventLabel, pluginDocsHelp, scopeTokensForHost } from "@aglyn/aglyn";
19
19
  import { CardDisplay } from "@aglyn/shared-ui-jsx";
20
20
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
21
21
  import { useFirestore, useFirestoreCollection, useOrgDataScope } from "@aglyn/tenant-feature-instance";
@@ -25,6 +25,7 @@ import { useCallback, useMemo, useState } from "react";
25
25
  import { ORG_AUTOMATIONS_COLLECTION, ORG_AUTOMATIONS_MAX, orgAutomationPausedHostIds } from "../model/org-automations.js";
26
26
  import HostRunHistoryCard from "./host-run-history-card.component.js";
27
27
  import { useOrgAutomationsApi } from "./use-org-automations-api.js";
28
+ import { HOST_ACTION_STEP_LABELS } from "../model/host-actions.js";
28
29
  /**
29
30
  * THE ORGANIZATION'S AUTOMATIONS THAT RUN ON THIS SITE (AGL-3302), on the
30
31
  * site's own Actions section: read-only, with the one control that belongs
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/site-org-automations-panel.component.tsx"],"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'use client'\n\nimport {\n HOST_ACTION_STEP_LABELS,\n type HostActionStepType,\n hostEventLabel,\n pluginDocsHelp,\n scopeTokensForHost,\n} from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useFirestoreCollection,\n useOrgDataScope,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query, where } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n ORG_AUTOMATIONS_COLLECTION,\n ORG_AUTOMATIONS_MAX,\n orgAutomationPausedHostIds,\n type OrgAutomationRow,\n} from '../model/org-automations'\nimport HostRunHistoryCard from './host-run-history-card.component'\nimport { useOrgAutomationsApi } from './use-org-automations-api'\n\n/**\n * THE ORGANIZATION'S AUTOMATIONS THAT RUN ON THIS SITE (AGL-3302), on the\n * site's own Actions section: read-only, with the one control that belongs\n * to the site — pausing an automation here, and resuming it.\n *\n * Read with the site's own scope tokens, so the listener is provable for a\n * collaborator scoped to this site as well as for an org-wide member, and\n * only the live ones placed here come back. Editing is the organization's,\n * on its own hub; a pause is the site's, through `automations/pause`, which\n * admits the site's admins and editors and refuses anyone else with its own\n * words.\n *\n * Renders nothing while there is nothing: a site the organization runs no\n * automation on has no panel to explain.\n */\nexport function SiteOrgAutomationsPanel(props: { hostId: string }) {\n const { hostId } = props\n const firestore = useFirestore()\n const api = useOrgAutomationsApi()\n const { enqueueSnackbar } = useSnackbar()\n const { scope } = useOrgDataScope({ hostId })\n const tokens = useMemo(() => scopeTokensForHost(hostId), [hostId])\n const { data: rows } = useFirestoreCollection<OrgAutomationRow>(\n () =>\n scope\n ? query(\n collection(firestore, scope[0], scope[1], ORG_AUTOMATIONS_COLLECTION),\n where('deletedAt', '==', null),\n where('visibleTo', 'array-contains-any', tokens),\n limit(ORG_AUTOMATIONS_MAX + 1),\n )\n : null,\n [firestore, scope, tokens],\n { idField: '$id' },\n )\n const automations = useMemo(\n () =>\n [...(rows ?? [])].sort((a, b) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n ),\n [rows],\n )\n const [busy, setBusy] = useState<string | null>(null)\n const [runsFor, setRunsFor] = useState<OrgAutomationRow | null>(null)\n\n const setPaused = useCallback(\n async (row: OrgAutomationRow, paused: boolean) => {\n setBusy(row.$id)\n try {\n await api('pause', { hostId, automationId: row.$id, paused })\n enqueueSnackbar(\n paused\n ? `“${row.name}” is paused on this site`\n : `“${row.name}” runs on this site again`,\n { variant: 'success', persist: false },\n )\n } catch (error) {\n enqueueSnackbar(\n (error as Error)?.message || 'The request could not be completed',\n { variant: 'warning', persist: false },\n )\n } finally {\n setBusy(null)\n }\n },\n [api, hostId, enqueueSnackbar],\n )\n\n if (!automations.length) return null\n return (\n <CardDisplay\n header={'Org automations on this site'}\n help={pluginDocsHelp('orgAutomations', { anchor: '#pause-it-on-one-site' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Your organization runs these on this site beside the site’s own ' +\n 'actions. Each run sends from this site and counts on its action ' +\n 'runs. Pausing one stops it here — and anyone waiting inside it ' +\n 'here — and leaves every other site alone.'}\n </Typography>\n {automations.map((row) => {\n const paused = orgAutomationPausedHostIds(row).includes(hostId)\n const off = row.enabled === false\n return (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`on ${hostEventLabel(row.trigger?.event)} · ${(row.steps ?? [])\n .map(\n (step) =>\n HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ??\n step.type,\n )\n .join(' → ')}`}\n </Typography>\n </Stack>\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={off ? 'default' : paused ? 'warning' : 'success'}\n label={off ? 'Switched off' : paused ? 'Paused here' : 'Runs here'}\n />\n <Button\n size=\"small\"\n disabled={busy === row.$id}\n onClick={() => void setPaused(row, !paused)}\n >\n {paused ? 'Resume here' : 'Pause here'}\n </Button>\n <Button size=\"small\" onClick={() => setRunsFor(row)}>\n {'Runs'}\n </Button>\n </Stack>\n )\n })}\n </Stack>\n <Dialog\n open={Boolean(runsFor)}\n onClose={() => setRunsFor(null)}\n // md: the history's four columns fit without the table scrolling.\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>{`Runs — ${runsFor?.name ?? ''}`}</DialogTitle>\n <DialogContent>\n {runsFor ? (\n <HostRunHistoryCard\n hostId={hostId}\n targetId={runsFor.$id}\n targetName={runsFor.name ?? ''}\n header=\"Recent runs on this site\"\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setRunsFor(null)}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nSiteOrgAutomationsPanel.displayName = 'SiteOrgAutomationsPanel'\n\nexport default SiteOrgAutomationsPanel\n"],"names":["HOST_ACTION_STEP_LABELS","hostEventLabel","pluginDocsHelp","scopeTokensForHost","CardDisplay","useSnackbar","useFirestore","useFirestoreCollection","useOrgDataScope","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Typography","collection","limit","query","where","useCallback","useMemo","useState","ORG_AUTOMATIONS_COLLECTION","ORG_AUTOMATIONS_MAX","orgAutomationPausedHostIds","HostRunHistoryCard","useOrgAutomationsApi","SiteOrgAutomationsPanel","props","runsFor","hostId","firestore","api","enqueueSnackbar","scope","tokens","data","rows","idField","automations","sort","a","b","String","name","localeCompare","busy","setBusy","setRunsFor","setPaused","row","paused","$id","automationId","variant","persist","error","message","length","header","help","anchor","contentGutterX","contentGutterY","spacing","color","map","includes","off","enabled","direction","sx","alignItems","flex","minWidth","noWrap","trigger","event","steps","step","type","join","size","label","disabled","onClick","open","Boolean","onClose","maxWidth","fullWidth","targetId","targetName","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/site-org-automations-panel.component.tsx"],"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'use client'\n\nimport { hostEventLabel, pluginDocsHelp, scopeTokensForHost } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useFirestoreCollection,\n useOrgDataScope,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query, where } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n ORG_AUTOMATIONS_COLLECTION,\n ORG_AUTOMATIONS_MAX,\n orgAutomationPausedHostIds,\n type OrgAutomationRow,\n} from '../model/org-automations'\nimport HostRunHistoryCard from './host-run-history-card.component'\nimport { useOrgAutomationsApi } from './use-org-automations-api'\nimport { HOST_ACTION_STEP_LABELS, type HostActionStepType } from '../model/host-actions'\n\n/**\n * THE ORGANIZATION'S AUTOMATIONS THAT RUN ON THIS SITE (AGL-3302), on the\n * site's own Actions section: read-only, with the one control that belongs\n * to the site — pausing an automation here, and resuming it.\n *\n * Read with the site's own scope tokens, so the listener is provable for a\n * collaborator scoped to this site as well as for an org-wide member, and\n * only the live ones placed here come back. Editing is the organization's,\n * on its own hub; a pause is the site's, through `automations/pause`, which\n * admits the site's admins and editors and refuses anyone else with its own\n * words.\n *\n * Renders nothing while there is nothing: a site the organization runs no\n * automation on has no panel to explain.\n */\nexport function SiteOrgAutomationsPanel(props: { hostId: string }) {\n const { hostId } = props\n const firestore = useFirestore()\n const api = useOrgAutomationsApi()\n const { enqueueSnackbar } = useSnackbar()\n const { scope } = useOrgDataScope({ hostId })\n const tokens = useMemo(() => scopeTokensForHost(hostId), [hostId])\n const { data: rows } = useFirestoreCollection<OrgAutomationRow>(\n () =>\n scope\n ? query(\n collection(firestore, scope[0], scope[1], ORG_AUTOMATIONS_COLLECTION),\n where('deletedAt', '==', null),\n where('visibleTo', 'array-contains-any', tokens),\n limit(ORG_AUTOMATIONS_MAX + 1),\n )\n : null,\n [firestore, scope, tokens],\n { idField: '$id' },\n )\n const automations = useMemo(\n () =>\n [...(rows ?? [])].sort((a, b) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n ),\n [rows],\n )\n const [busy, setBusy] = useState<string | null>(null)\n const [runsFor, setRunsFor] = useState<OrgAutomationRow | null>(null)\n\n const setPaused = useCallback(\n async (row: OrgAutomationRow, paused: boolean) => {\n setBusy(row.$id)\n try {\n await api('pause', { hostId, automationId: row.$id, paused })\n enqueueSnackbar(\n paused\n ? `“${row.name}” is paused on this site`\n : `“${row.name}” runs on this site again`,\n { variant: 'success', persist: false },\n )\n } catch (error) {\n enqueueSnackbar(\n (error as Error)?.message || 'The request could not be completed',\n { variant: 'warning', persist: false },\n )\n } finally {\n setBusy(null)\n }\n },\n [api, hostId, enqueueSnackbar],\n )\n\n if (!automations.length) return null\n return (\n <CardDisplay\n header={'Org automations on this site'}\n help={pluginDocsHelp('orgAutomations', { anchor: '#pause-it-on-one-site' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Your organization runs these on this site beside the site’s own ' +\n 'actions. Each run sends from this site and counts on its action ' +\n 'runs. Pausing one stops it here — and anyone waiting inside it ' +\n 'here — and leaves every other site alone.'}\n </Typography>\n {automations.map((row) => {\n const paused = orgAutomationPausedHostIds(row).includes(hostId)\n const off = row.enabled === false\n return (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Stack sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`on ${hostEventLabel(row.trigger?.event)} · ${(row.steps ?? [])\n .map(\n (step) =>\n HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ??\n step.type,\n )\n .join(' → ')}`}\n </Typography>\n </Stack>\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={off ? 'default' : paused ? 'warning' : 'success'}\n label={off ? 'Switched off' : paused ? 'Paused here' : 'Runs here'}\n />\n <Button\n size=\"small\"\n disabled={busy === row.$id}\n onClick={() => void setPaused(row, !paused)}\n >\n {paused ? 'Resume here' : 'Pause here'}\n </Button>\n <Button size=\"small\" onClick={() => setRunsFor(row)}>\n {'Runs'}\n </Button>\n </Stack>\n )\n })}\n </Stack>\n <Dialog\n open={Boolean(runsFor)}\n onClose={() => setRunsFor(null)}\n // md: the history's four columns fit without the table scrolling.\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>{`Runs — ${runsFor?.name ?? ''}`}</DialogTitle>\n <DialogContent>\n {runsFor ? (\n <HostRunHistoryCard\n hostId={hostId}\n targetId={runsFor.$id}\n targetName={runsFor.name ?? ''}\n header=\"Recent runs on this site\"\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setRunsFor(null)}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nSiteOrgAutomationsPanel.displayName = 'SiteOrgAutomationsPanel'\n\nexport default SiteOrgAutomationsPanel\n"],"names":["hostEventLabel","pluginDocsHelp","scopeTokensForHost","CardDisplay","useSnackbar","useFirestore","useFirestoreCollection","useOrgDataScope","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Typography","collection","limit","query","where","useCallback","useMemo","useState","ORG_AUTOMATIONS_COLLECTION","ORG_AUTOMATIONS_MAX","orgAutomationPausedHostIds","HostRunHistoryCard","useOrgAutomationsApi","HOST_ACTION_STEP_LABELS","SiteOrgAutomationsPanel","props","runsFor","hostId","firestore","api","enqueueSnackbar","scope","tokens","data","rows","idField","automations","sort","a","b","String","name","localeCompare","busy","setBusy","setRunsFor","setPaused","row","paused","$id","automationId","variant","persist","error","message","length","header","help","anchor","contentGutterX","contentGutterY","spacing","color","map","includes","off","enabled","direction","sx","alignItems","flex","minWidth","noWrap","trigger","event","steps","step","type","join","size","label","disabled","onClick","open","Boolean","onClose","maxWidth","fullWidth","targetId","targetName","displayName"],"mappings":"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"}
@@ -88,9 +88,9 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
88
88
  * and every site's own workflows, actions and webhooks listed with the site
89
89
  * each belongs to.
90
90
  *
91
- * No run allowance line: the allowance is each site's, and an org
92
- * automation's runs count on the site they run on — each site's own
93
- * Automation reports its meter.
91
+ * The run allowance line heads the workflows and actions lists here too: the
92
+ * allowance is the workspace's (AGL-3472), every site's runs spend it, and
93
+ * this is the page that lists them all.
94
94
  */ function orgSectionBody(section, mount, org, canEdit) {
95
95
  switch(section){
96
96
  case 'automations':
@@ -101,10 +101,21 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
101
101
  });
102
102
  case 'workflows':
103
103
  case 'actions':
104
- return /*#__PURE__*/ _jsx(OrgSiteAutomationList, {
105
- mount: mount,
106
- kind: section,
107
- canRead: true
104
+ return /*#__PURE__*/ _jsxs(Stack, {
105
+ spacing: 1,
106
+ children: [
107
+ /*#__PURE__*/ _jsx(RunQuotaLine, {
108
+ hostId: null,
109
+ orgId: mount.orgId,
110
+ org: org,
111
+ counter: section === 'workflows' ? 'workflowRuns' : 'actionRuns'
112
+ }),
113
+ /*#__PURE__*/ _jsx(OrgSiteAutomationList, {
114
+ mount: mount,
115
+ kind: section,
116
+ canRead: true
117
+ })
118
+ ]
108
119
  });
109
120
  case 'webhooks':
110
121
  // A webhook holds its site's secret: the rules admit a site's admins
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflows-console-page.tsx"],"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'use client'\n\nimport type { AglynOrgBilling, ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport { Stack } from '@mui/material'\nimport { useMemo, type ReactNode } from 'react'\nimport HostActionsCard from './host-actions-card.component'\nimport HostWebhooksCard from './host-webhooks-card.component'\nimport HostWorkflowsCard from './host-workflows-card.component'\nimport OrgAutomationsCard from './org-automations-card.component'\nimport OrgSiteAutomationList from './org-site-automation-list.component'\nimport RunQuotaLine from './run-quota-line.component'\nimport SiteOrgAutomationsPanel from './site-org-automations-panel.component'\nimport type { WorkflowsConsoleSectionId } from './workflows-console-sections'\nimport {\n workflowsOrgMount,\n type WorkflowsOrgMount,\n} from './workflows-org-mount'\n\n/**\n * The body of one workflows section, built only when that section is the one\n * being read (AGL-2501).\n *\n * A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`\n * would CONSTRUCT all three every render, and each card opens its Firestore\n * listens on mount — which is the entire cost this page exists to stop paying.\n * Only the returned branch is ever built.\n */\nfunction sectionBody(\n section: WorkflowsConsoleSectionId,\n hostId: string,\n org: Partial<AglynOrgBilling> | undefined,\n): ReactNode {\n switch (section) {\n case 'workflows':\n return (\n /*\n * `N runs this month · M included` (AGL-2171), which the run-history\n * mockup puts opposite the heading. The quota silently stops\n * automations running once it is reached, and the only place it was\n * reported was the Billing page.\n */\n <Stack spacing={1}>\n <RunQuotaLine hostId={hostId} org={org} counter=\"workflowRuns\" />\n <HostWorkflowsCard hostId={hostId} org={org} />\n </Stack>\n )\n case 'actions':\n return (\n <Stack spacing={1}>\n {/* `actionRunsPerMonth` had NO customer-facing surface at all\n before this — staff panel and usage-alerts only. */}\n <RunQuotaLine hostId={hostId} org={org} counter=\"actionRuns\" />\n <HostActionsCard hostId={hostId} org={org} />\n {/* What the organization runs on this site beside the site's own\n actions, and the site's pause for each (AGL-3302). */}\n <SiteOrgAutomationsPanel hostId={hostId} />\n </Stack>\n )\n case 'webhooks':\n return <HostWebhooksCard hostId={hostId} org={org} />\n default:\n return null\n }\n}\n\n/**\n * The body of one ORGANIZATION-level section (AGL-3302): the org automations,\n * and every site's own workflows, actions and webhooks listed with the site\n * each belongs to.\n *\n * No run allowance line: the allowance is each site's, and an org\n * automation's runs count on the site they run on — each site's own\n * Automation reports its meter.\n */\nfunction orgSectionBody(\n section: WorkflowsConsoleSectionId,\n mount: WorkflowsOrgMount,\n org: Partial<AglynOrgBilling> | undefined,\n canEdit: boolean,\n): ReactNode {\n switch (section) {\n case 'automations':\n return <OrgAutomationsCard mount={mount} org={org} canEdit={canEdit} />\n case 'workflows':\n case 'actions':\n return <OrgSiteAutomationList mount={mount} kind={section} canRead />\n case 'webhooks':\n // A webhook holds its site's secret: the rules admit a site's admins\n // and editors, which every org-wide member but a viewer is.\n return (\n <OrgSiteAutomationList mount={mount} kind=\"webhooks\" canRead={canEdit} />\n )\n default:\n return null\n }\n}\n\n/**\n * Automation page (AGL-101/148/149 → AGL-395): the automation surface —\n * workflow builder, event-triggered actions, and webhooks — owned by the\n * workflows plugin and rendered by the shell's generic plugin route. Each card\n * runs its own entitlement check (workflows / actions / webhooks are distinct\n * plan flags), so the shell's resolved `org` doc flows into all three.\n *\n * Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so\n * this is not a read saving — `workflows-console-read-cost.spec.tsx` was\n * written BEFORE the conversion precisely to hold that line, and reports the\n * same counts after. What routing adds is that the URL names the section: it is\n * linkable, the back button walks sections, the breadcrumb says where you are,\n * and \"mount only what is open\" is structural rather than a `lazy` flag.\n *\n * The section ids are unchanged by the Automation rename: they are the\n * addresses people bookmark and the docs link to. A workflow belongs to the\n * site and an ACTION belongs to the site too; an interaction belongs to the\n * document that holds it and is edited on the element, not here.\n */\nexport function WorkflowsConsolePage(props: ConsolePluginPageProps) {\n const { hostId, orgMount, org, permissions, section, sections, basePath } =\n props\n /*\n * Mounted with no site: the organization's hub (AGL-3302). Memoised so the\n * cards below, which key their listeners on the mount's org, see one object.\n */\n const mount = useMemo(\n () => (hostId == null ? workflowsOrgMount(orgMount, basePath) : null),\n [hostId, orgMount, basePath],\n )\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare hub URL\n * to the landing section and holds a spinner while it does, so this state is\n * transient — and rendering a default section here instead would pay for its\n * listens on a URL that is already being replaced.\n */\n if (!section || !sections?.length || !basePath) return null\n\n if (hostId == null) {\n // No site and no org to stand in for it: nothing this page can scope.\n if (!mount) return null\n return (\n <HubSections sections={sections}>\n {orgSectionBody(\n section as WorkflowsConsoleSectionId,\n mount,\n org,\n // An org viewer's `editHosts` is false; every other org role's is\n // true. The server routes decide — this keeps a viewer from being\n // offered controls that are about to refuse them.\n permissions?.editHosts !== false,\n )}\n </HubSections>\n )\n }\n\n return (\n <HubSections sections={sections}>\n {sectionBody(section as WorkflowsConsoleSectionId, hostId, org)}\n </HubSections>\n )\n}\nWorkflowsConsolePage.displayName = 'WorkflowsConsolePage'\n\nexport default WorkflowsConsolePage\n"],"names":["HubSections","Stack","useMemo","HostActionsCard","HostWebhooksCard","HostWorkflowsCard","OrgAutomationsCard","OrgSiteAutomationList","RunQuotaLine","SiteOrgAutomationsPanel","workflowsOrgMount","sectionBody","section","hostId","org","spacing","counter","orgSectionBody","mount","canEdit","kind","canRead","WorkflowsConsolePage","props","orgMount","permissions","sections","basePath","length","editHosts","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,WAAW,QAAQ,wBAAuB;AACnD,SAASC,KAAK,QAAQ,gBAAe;AACrC,SAASC,OAAO,QAAwB,QAAO;AAC/C,OAAOC,qBAAqB,mCAA+B;AAC3D,OAAOC,sBAAsB,oCAAgC;AAC7D,OAAOC,uBAAuB,qCAAiC;AAC/D,OAAOC,wBAAwB,sCAAkC;AACjE,OAAOC,2BAA2B,0CAAsC;AACxE,OAAOC,kBAAkB,gCAA4B;AACrD,OAAOC,6BAA6B,4CAAwC;AAE5E,SACEC,iBAAiB,QAEZ,2BAAuB;AAE9B;;;;;;;;CAQC,GACD,SAASC,YACPC,OAAkC,EAClCC,MAAc,EACdC,GAAyC;IAEzC,OAAQF;QACN,KAAK;YACH,OACE;;;;;SAKC,iBACD,MAACX;gBAAMc,SAAS;;kCACd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACX;wBAAkBQ,QAAQA;wBAAQC,KAAKA;;;;QAG9C,KAAK;YACH,qBACE,MAACb;gBAAMc,SAAS;;kCAGd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACb;wBAAgBU,QAAQA;wBAAQC,KAAKA;;kCAGtC,KAACL;wBAAwBI,QAAQA;;;;QAGvC,KAAK;YACH,qBAAO,KAACT;gBAAiBS,QAAQA;gBAAQC,KAAKA;;QAChD;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;CAQC,GACD,SAASG,eACPL,OAAkC,EAClCM,KAAwB,EACxBJ,GAAyC,EACzCK,OAAgB;IAEhB,OAAQP;QACN,KAAK;YACH,qBAAO,KAACN;gBAAmBY,OAAOA;gBAAOJ,KAAKA;gBAAKK,SAASA;;QAC9D,KAAK;QACL,KAAK;YACH,qBAAO,KAACZ;gBAAsBW,OAAOA;gBAAOE,MAAMR;gBAASS,OAAO;;QACpE,KAAK;YACH,qEAAqE;YACrE,4DAA4D;YAC5D,qBACE,KAACd;gBAAsBW,OAAOA;gBAAOE,MAAK;gBAAWC,SAASF;;QAElE;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASG,qBAAqBC,KAA6B;IAChE,MAAM,EAAEV,MAAM,EAAEW,QAAQ,EAAEV,GAAG,EAAEW,WAAW,EAAEb,OAAO,EAAEc,QAAQ,EAAEC,QAAQ,EAAE,GACvEJ;IACF;;;GAGC,GACD,MAAML,QAAQhB,QACZ,IAAOW,UAAU,OAAOH,kBAAkBc,UAAUG,YAAY,MAChE;QAACd;QAAQW;QAAUG;KAAS;IAG9B;;;;;GAKC,GACD,IAAI,CAACf,WAAW,EAACc,4BAAAA,SAAUE,MAAM,KAAI,CAACD,UAAU,OAAO;IAEvD,IAAId,UAAU,MAAM;QAClB,sEAAsE;QACtE,IAAI,CAACK,OAAO,OAAO;QACnB,qBACE,KAAClB;YAAY0B,UAAUA;sBACpBT,eACCL,SACAM,OACAJ,KACA,kEAAkE;YAClE,kEAAkE;YAClE,kDAAkD;YAClDW,CAAAA,+BAAAA,YAAaI,SAAS,MAAK;;IAInC;IAEA,qBACE,KAAC7B;QAAY0B,UAAUA;kBACpBf,YAAYC,SAAsCC,QAAQC;;AAGjE;AACAQ,qBAAqBQ,WAAW,GAAG;AAEnC,eAAeR,qBAAoB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflows-console-page.tsx"],"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'use client'\n\nimport type { AglynOrgBilling, ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport { Stack } from '@mui/material'\nimport { useMemo, type ReactNode } from 'react'\nimport HostActionsCard from './host-actions-card.component'\nimport HostWebhooksCard from './host-webhooks-card.component'\nimport HostWorkflowsCard from './host-workflows-card.component'\nimport OrgAutomationsCard from './org-automations-card.component'\nimport OrgSiteAutomationList from './org-site-automation-list.component'\nimport RunQuotaLine from './run-quota-line.component'\nimport SiteOrgAutomationsPanel from './site-org-automations-panel.component'\nimport type { WorkflowsConsoleSectionId } from './workflows-console-sections'\nimport {\n workflowsOrgMount,\n type WorkflowsOrgMount,\n} from './workflows-org-mount'\n\n/**\n * The body of one workflows section, built only when that section is the one\n * being read (AGL-2501).\n *\n * A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`\n * would CONSTRUCT all three every render, and each card opens its Firestore\n * listens on mount — which is the entire cost this page exists to stop paying.\n * Only the returned branch is ever built.\n */\nfunction sectionBody(\n section: WorkflowsConsoleSectionId,\n hostId: string,\n org: Partial<AglynOrgBilling> | undefined,\n): ReactNode {\n switch (section) {\n case 'workflows':\n return (\n /*\n * `N runs this month · M included` (AGL-2171), which the run-history\n * mockup puts opposite the heading. The quota silently stops\n * automations running once it is reached, and the only place it was\n * reported was the Billing page.\n */\n <Stack spacing={1}>\n <RunQuotaLine hostId={hostId} org={org} counter=\"workflowRuns\" />\n <HostWorkflowsCard hostId={hostId} org={org} />\n </Stack>\n )\n case 'actions':\n return (\n <Stack spacing={1}>\n {/* `actionRunsPerMonth` had NO customer-facing surface at all\n before this — staff panel and usage-alerts only. */}\n <RunQuotaLine hostId={hostId} org={org} counter=\"actionRuns\" />\n <HostActionsCard hostId={hostId} org={org} />\n {/* What the organization runs on this site beside the site's own\n actions, and the site's pause for each (AGL-3302). */}\n <SiteOrgAutomationsPanel hostId={hostId} />\n </Stack>\n )\n case 'webhooks':\n return <HostWebhooksCard hostId={hostId} org={org} />\n default:\n return null\n }\n}\n\n/**\n * The body of one ORGANIZATION-level section (AGL-3302): the org automations,\n * and every site's own workflows, actions and webhooks listed with the site\n * each belongs to.\n *\n * The run allowance line heads the workflows and actions lists here too: the\n * allowance is the workspace's (AGL-3472), every site's runs spend it, and\n * this is the page that lists them all.\n */\nfunction orgSectionBody(\n section: WorkflowsConsoleSectionId,\n mount: WorkflowsOrgMount,\n org: Partial<AglynOrgBilling> | undefined,\n canEdit: boolean,\n): ReactNode {\n switch (section) {\n case 'automations':\n return <OrgAutomationsCard mount={mount} org={org} canEdit={canEdit} />\n case 'workflows':\n case 'actions':\n return (\n <Stack spacing={1}>\n <RunQuotaLine\n hostId={null}\n orgId={mount.orgId}\n org={org}\n counter={section === 'workflows' ? 'workflowRuns' : 'actionRuns'}\n />\n <OrgSiteAutomationList mount={mount} kind={section} canRead />\n </Stack>\n )\n case 'webhooks':\n // A webhook holds its site's secret: the rules admit a site's admins\n // and editors, which every org-wide member but a viewer is.\n return (\n <OrgSiteAutomationList mount={mount} kind=\"webhooks\" canRead={canEdit} />\n )\n default:\n return null\n }\n}\n\n/**\n * Automation page (AGL-101/148/149 → AGL-395): the automation surface —\n * workflow builder, event-triggered actions, and webhooks — owned by the\n * workflows plugin and rendered by the shell's generic plugin route. Each card\n * runs its own entitlement check (workflows / actions / webhooks are distinct\n * plan flags), so the shell's resolved `org` doc flows into all three.\n *\n * Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so\n * this is not a read saving — `workflows-console-read-cost.spec.tsx` was\n * written BEFORE the conversion precisely to hold that line, and reports the\n * same counts after. What routing adds is that the URL names the section: it is\n * linkable, the back button walks sections, the breadcrumb says where you are,\n * and \"mount only what is open\" is structural rather than a `lazy` flag.\n *\n * The section ids are unchanged by the Automation rename: they are the\n * addresses people bookmark and the docs link to. A workflow belongs to the\n * site and an ACTION belongs to the site too; an interaction belongs to the\n * document that holds it and is edited on the element, not here.\n */\nexport function WorkflowsConsolePage(props: ConsolePluginPageProps) {\n const { hostId, orgMount, org, permissions, section, sections, basePath } =\n props\n /*\n * Mounted with no site: the organization's hub (AGL-3302). Memoised so the\n * cards below, which key their listeners on the mount's org, see one object.\n */\n const mount = useMemo(\n () => (hostId == null ? workflowsOrgMount(orgMount, basePath) : null),\n [hostId, orgMount, basePath],\n )\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare hub URL\n * to the landing section and holds a spinner while it does, so this state is\n * transient — and rendering a default section here instead would pay for its\n * listens on a URL that is already being replaced.\n */\n if (!section || !sections?.length || !basePath) return null\n\n if (hostId == null) {\n // No site and no org to stand in for it: nothing this page can scope.\n if (!mount) return null\n return (\n <HubSections sections={sections}>\n {orgSectionBody(\n section as WorkflowsConsoleSectionId,\n mount,\n org,\n // An org viewer's `editHosts` is false; every other org role's is\n // true. The server routes decide — this keeps a viewer from being\n // offered controls that are about to refuse them.\n permissions?.editHosts !== false,\n )}\n </HubSections>\n )\n }\n\n return (\n <HubSections sections={sections}>\n {sectionBody(section as WorkflowsConsoleSectionId, hostId, org)}\n </HubSections>\n )\n}\nWorkflowsConsolePage.displayName = 'WorkflowsConsolePage'\n\nexport default WorkflowsConsolePage\n"],"names":["HubSections","Stack","useMemo","HostActionsCard","HostWebhooksCard","HostWorkflowsCard","OrgAutomationsCard","OrgSiteAutomationList","RunQuotaLine","SiteOrgAutomationsPanel","workflowsOrgMount","sectionBody","section","hostId","org","spacing","counter","orgSectionBody","mount","canEdit","orgId","kind","canRead","WorkflowsConsolePage","props","orgMount","permissions","sections","basePath","length","editHosts","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,WAAW,QAAQ,wBAAuB;AACnD,SAASC,KAAK,QAAQ,gBAAe;AACrC,SAASC,OAAO,QAAwB,QAAO;AAC/C,OAAOC,qBAAqB,mCAA+B;AAC3D,OAAOC,sBAAsB,oCAAgC;AAC7D,OAAOC,uBAAuB,qCAAiC;AAC/D,OAAOC,wBAAwB,sCAAkC;AACjE,OAAOC,2BAA2B,0CAAsC;AACxE,OAAOC,kBAAkB,gCAA4B;AACrD,OAAOC,6BAA6B,4CAAwC;AAE5E,SACEC,iBAAiB,QAEZ,2BAAuB;AAE9B;;;;;;;;CAQC,GACD,SAASC,YACPC,OAAkC,EAClCC,MAAc,EACdC,GAAyC;IAEzC,OAAQF;QACN,KAAK;YACH,OACE;;;;;SAKC,iBACD,MAACX;gBAAMc,SAAS;;kCACd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACX;wBAAkBQ,QAAQA;wBAAQC,KAAKA;;;;QAG9C,KAAK;YACH,qBACE,MAACb;gBAAMc,SAAS;;kCAGd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACb;wBAAgBU,QAAQA;wBAAQC,KAAKA;;kCAGtC,KAACL;wBAAwBI,QAAQA;;;;QAGvC,KAAK;YACH,qBAAO,KAACT;gBAAiBS,QAAQA;gBAAQC,KAAKA;;QAChD;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;CAQC,GACD,SAASG,eACPL,OAAkC,EAClCM,KAAwB,EACxBJ,GAAyC,EACzCK,OAAgB;IAEhB,OAAQP;QACN,KAAK;YACH,qBAAO,KAACN;gBAAmBY,OAAOA;gBAAOJ,KAAKA;gBAAKK,SAASA;;QAC9D,KAAK;QACL,KAAK;YACH,qBACE,MAAClB;gBAAMc,SAAS;;kCACd,KAACP;wBACCK,QAAQ;wBACRO,OAAOF,MAAME,KAAK;wBAClBN,KAAKA;wBACLE,SAASJ,YAAY,cAAc,iBAAiB;;kCAEtD,KAACL;wBAAsBW,OAAOA;wBAAOG,MAAMT;wBAASU,OAAO;;;;QAGjE,KAAK;YACH,qEAAqE;YACrE,4DAA4D;YAC5D,qBACE,KAACf;gBAAsBW,OAAOA;gBAAOG,MAAK;gBAAWC,SAASH;;QAElE;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASI,qBAAqBC,KAA6B;IAChE,MAAM,EAAEX,MAAM,EAAEY,QAAQ,EAAEX,GAAG,EAAEY,WAAW,EAAEd,OAAO,EAAEe,QAAQ,EAAEC,QAAQ,EAAE,GACvEJ;IACF;;;GAGC,GACD,MAAMN,QAAQhB,QACZ,IAAOW,UAAU,OAAOH,kBAAkBe,UAAUG,YAAY,MAChE;QAACf;QAAQY;QAAUG;KAAS;IAG9B;;;;;GAKC,GACD,IAAI,CAAChB,WAAW,EAACe,4BAAAA,SAAUE,MAAM,KAAI,CAACD,UAAU,OAAO;IAEvD,IAAIf,UAAU,MAAM;QAClB,sEAAsE;QACtE,IAAI,CAACK,OAAO,OAAO;QACnB,qBACE,KAAClB;YAAY2B,UAAUA;sBACpBV,eACCL,SACAM,OACAJ,KACA,kEAAkE;YAClE,kEAAkE;YAClE,kDAAkD;YAClDY,CAAAA,+BAAAA,YAAaI,SAAS,MAAK;;IAInC;IAEA,qBACE,KAAC9B;QAAY2B,UAAUA;kBACpBhB,YAAYC,SAAsCC,QAAQC;;AAGjE;AACAS,qBAAqBQ,WAAW,GAAG;AAEnC,eAAeR,qBAAoB"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * The automation editor's DECLARATIONS: what another surface reads of this
19
+ * plugin before any of its own surfaces load, in both apps and in the
20
+ * console's browser.
21
+ *
22
+ * The checks of the server steps an automation holds (AGL-3080), so a plugin
23
+ * that writes an automation it does not edit — a recipe installed into a
24
+ * site, a drafted automation graded before it is handed over — refuses what
25
+ * this editor would (`validateStoredInteraction`). Light by construction: the
26
+ * checks are pure, and nothing here reaches Firestore or React.
27
+ */
28
+ export declare function registerWorkflowsDeclarations(): void;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ // The seam from its own module, not the plugin-manager barrel: both apps load
17
+ // this at boot, and the barrel reaches the client contexts.
18
+ import { registerInteractionStepChecks } from "@aglyn/aglyn/plugin-manager/interaction-step-checks";
19
+ import { BUNDLE_ID } from "./constants/bundle-common.js";
20
+ import { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from "./model/host-actions.js";
21
+ /**
22
+ * The automation editor's DECLARATIONS: what another surface reads of this
23
+ * plugin before any of its own surfaces load, in both apps and in the
24
+ * console's browser.
25
+ *
26
+ * The checks of the server steps an automation holds (AGL-3080), so a plugin
27
+ * that writes an automation it does not edit — a recipe installed into a
28
+ * site, a drafted automation graded before it is handed over — refuses what
29
+ * this editor would (`validateStoredInteraction`). Light by construction: the
30
+ * checks are pure, and nothing here reaches Firestore or React.
31
+ */ export function registerWorkflowsDeclarations() {
32
+ registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, {
33
+ pluginId: BUNDLE_ID
34
+ });
35
+ }
36
+
37
+ //# sourceMappingURL=declarations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.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// The seam from its own module, not the plugin-manager barrel: both apps load\n// this at boot, and the barrel reaches the client contexts.\nimport { registerInteractionStepChecks } from '@aglyn/aglyn/plugin-manager/interaction-step-checks'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from './model/host-actions'\n\n/**\n * The automation editor's DECLARATIONS: what another surface reads of this\n * plugin before any of its own surfaces load, in both apps and in the\n * console's browser.\n *\n * The checks of the server steps an automation holds (AGL-3080), so a plugin\n * that writes an automation it does not edit — a recipe installed into a\n * site, a drafted automation graded before it is handed over — refuses what\n * this editor would (`validateStoredInteraction`). Light by construction: the\n * checks are pure, and nothing here reaches Firestore or React.\n */\nexport function registerWorkflowsDeclarations(): void {\n registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, { pluginId: BUNDLE_ID })\n}\n"],"names":["registerInteractionStepChecks","BUNDLE_ID","hostActionStepProblem","SERVER_ACTION_STEP_TYPES","registerWorkflowsDeclarations","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8EAA8E;AAC9E,4DAA4D;AAC5D,SAASA,6BAA6B,QAAQ,sDAAqD;AACnG,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,qBAAqB,EAAEC,wBAAwB,QAAQ,0BAAsB;AAEtF;;;;;;;;;;CAUC,GACD,OAAO,SAASC;IACdJ,8BAA8BG,0BAA0BD,uBAAuB;QAAEG,UAAUJ;IAAU;AACvG"}
@@ -19,7 +19,10 @@ import { registerHostEventListener } from "@aglyn/tenant-runtime/host-event-list
19
19
  import { registerVariableComputer } from "@aglyn/aglyn/plugin-manager/computed-variables";
20
20
  import { registerPluginDependentsSource } from "@aglyn/aglyn/plugin-manager/plugin-dependents";
21
21
  import { registerPluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
22
+ import { registerPluginTransferResource } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
22
23
  import { BUNDLE_ID } from "./constants/bundle-common.js";
24
+ import { ORG_AUTOMATION_PACKAGE_RULES, ORG_AUTOMATIONS_TRANSFER_KEY, orgAutomationDependencies, rawOrgAutomationContent, remapOrgAutomationIds } from "./transfer/org-automations-package.js";
25
+ /** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */ const automationsPackage = async ()=>(await import("./transfer/org-automations-package.server.js")).createOrgAutomationsPackage();
23
26
  /**
24
27
  * The automation engine, as the runtime hears it.
25
28
  *
@@ -68,6 +71,20 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
68
71
  * whose boot did not run it still registers the listener the first time a
69
72
  * plugin door loads. Registering twice replaces in place.
70
73
  */ export function registerWorkflowsServerDeclarations() {
74
+ // Org automations in a workspace package (AGL-3535).
75
+ registerPluginTransferResource(ORG_AUTOMATIONS_TRANSFER_KEY, {
76
+ items: async (ctx)=>(await automationsPackage()).items(ctx),
77
+ dependencies: (item)=>orgAutomationDependencies(rawOrgAutomationContent(item)),
78
+ remapIds: (item, idMap)=>remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),
79
+ readItems: async (ctx, ids)=>(await automationsPackage()).readItems(ctx, ids),
80
+ writeItems: async (ctx, items, writer)=>(await automationsPackage()).writeItems(ctx, items, writer),
81
+ revertItems: async (ctx, steps)=>(await automationsPackage()).revertItems(ctx, steps),
82
+ problems: async (ctx, write)=>(await automationsPackage()).problems(ctx, write),
83
+ referenceTargets: async (ctx, kinds)=>(await automationsPackage()).referenceTargets(ctx, kinds),
84
+ rules: ORG_AUTOMATION_PACKAGE_RULES
85
+ }, {
86
+ pluginId: BUNDLE_ID
87
+ });
71
88
  registerHostEventListener(BUNDLE_ID, workflowsHostEventListener);
72
89
  // The readers and the Admin SDK arrive with the first read, not with the boot.
73
90
  registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","BUNDLE_ID","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","lazyIndex","pluginId","kinds","find","request","findFunctionDependents","prepare","prepareWorkflowVariables","name","load","list","get"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,2EAA2E;AAC3E,oEAAoE;AACpE,SAEEA,yBAAyB,QACpB,6CAA4C;AACnD,SAASC,wBAAwB,QAAQ,iDAAgD;AACzF,SAASC,8BAA8B,QAAQ,gDAA+C;AAC9F,SACEC,yBAAyB,QAEpB,kDAAiD;AACxD,SAASC,SAAS,QAAQ,+BAA2B;AAErD;;;;;;CAMC,GACD,OAAO,MAAMC,6BAAgD;IAC3D,MAAMC,SAAQC,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAEC,OAAO;QAC3C,MAAM,CAAC,EAAEC,mBAAmB,EAAE,EAAE,EAAEC,mBAAmB,EAAE,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAC3E,MAAM,CAAC;YACP,MAAM,CAAC;SACR;QACD,gEAAgE;QAChE,OAAO,MAAMF,oBAAoBF,2BAAAA,QAASK,KAAK,EAAE,IAC/CJ,oBAAoBJ,QAAQC,OAAOC;IAEvC;IACA,MAAMO,YAAWT,MAAM,EAAEU,YAAY,EAAET,KAAK,EAAEC,OAAO,EAAEC,OAAO;;QAC5D,MAAM,CAAC,EAAEQ,eAAe,EAAE,EAAE,EAAEN,mBAAmB,EAAE,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YACvE,MAAM,CAAC;YACP,MAAM,CAAC;SACR;QACD,qEAAqE;QACrE,OAAO,MAAMF,4BAAoBF,2BAAAA,QAASK,KAAK,mBAAI;YAAEI,MAAM;QAAU,GAAG,IACtED,gBAAgBX,QAAQU,cAAcT,OAAOC;IAEjD;AACF,EAAC;AAED;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASW;IACdpB,0BAA0BI,WAAWC;IACrC,+EAA+E;IAC/EF,0BAA0B,YAAYkB,UAAU,wBAAwB;QAAEC,UAAUlB;IAAU;IAC9FD,0BAA0B,WAAWkB,UAAU,uBAAuB;QAAEC,UAAUlB;IAAU;IAC5FD,0BAA0B,UAAUkB,UAAU,sBAAsB;QAAEC,UAAUlB;IAAU;IAC1FF,+BACE;QACEqB,OAAO;YAAC;SAAW;QACnBC,MAAM,OAAOC,UACX,AAAC,CAAA,MAAM,MAAM,CAAC,kCAA8B,EAAGC,sBAAsB,CAACD;IAC1E,GACA;QAAEH,UAAUlB;IAAU;IAExBH,yBACE;QACE0B,SAAS,OAAOpB,SACd,AAAC,CAAA,MAAM,MAAM,CAAC,iCAA6B,EAAGqB,wBAAwB,CAACrB;IAC3E,GACA;QAAEe,UAAUlB;IAAU;AAE1B;AAIA,SAASiB,UAAUQ,IAAe;IAChC,MAAMC,OAAO,UAAY,AAAC,CAAA,MAAM,MAAM,CAAC,sCAAkC,CAAE,CAACD,KAAK;IACjF,OAAO;QACLE,MAAM,OAAON,UAAY,AAAC,CAAA,MAAMK,MAAK,EAAGC,IAAI,CAACN;QAC7CO,KAAK,OAAOP,UAAY,AAAC,CAAA,MAAMK,MAAK,EAAGE,GAAG,CAACP;IAC7C;AACF"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { registerPluginTransferResource } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport {\n ORG_AUTOMATION_PACKAGE_RULES,\n ORG_AUTOMATIONS_TRANSFER_KEY,\n orgAutomationDependencies,\n rawOrgAutomationContent,\n remapOrgAutomationIds,\n} from './transfer/org-automations-package'\n\n/** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */\nconst automationsPackage = async () =>\n (await import('./transfer/org-automations-package.server')).createOrgAutomationsPackage()\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n // Org automations in a workspace package (AGL-3535).\n registerPluginTransferResource(\n ORG_AUTOMATIONS_TRANSFER_KEY,\n {\n items: async (ctx) => (await automationsPackage()).items(ctx),\n dependencies: (item) => orgAutomationDependencies(rawOrgAutomationContent(item)),\n remapIds: (item, idMap) => remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),\n readItems: async (ctx, ids) => (await automationsPackage()).readItems(ctx, ids),\n writeItems: async (ctx, items, writer) => (await automationsPackage()).writeItems(ctx, items as never, writer),\n revertItems: async (ctx, steps) => (await automationsPackage()).revertItems(ctx, steps as never),\n problems: async (ctx, write) => (await automationsPackage()).problems(ctx, write as never),\n referenceTargets: async (ctx, kinds) => (await automationsPackage()).referenceTargets(ctx, kinds),\n rules: ORG_AUTOMATION_PACKAGE_RULES,\n },\n { pluginId: BUNDLE_ID },\n )\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","registerPluginTransferResource","BUNDLE_ID","ORG_AUTOMATION_PACKAGE_RULES","ORG_AUTOMATIONS_TRANSFER_KEY","orgAutomationDependencies","rawOrgAutomationContent","remapOrgAutomationIds","automationsPackage","createOrgAutomationsPackage","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","items","ctx","dependencies","item","remapIds","idMap","readItems","ids","writeItems","writer","revertItems","steps","problems","write","referenceTargets","kinds","rules","pluginId","lazyIndex","find","request","findFunctionDependents","prepare","prepareWorkflowVariables","name","load","list","get"],"mappings":"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"}
@@ -14,11 +14,12 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { type HostAction, type HostActionAlert, type HostActionStep, type HostFunction, type HostVariable, type PluginJobHostGate } from '@aglyn/aglyn/server';
17
+ import { type HostFunction, type HostVariable, type PluginJobHostGate } from '@aglyn/aglyn/server';
18
18
  import { type HostWorkflow } from '../model/workflows';
19
19
  import { type FlowEnrollment, type FlowSweepCursor, type FlowSweepResult } from './flow-enrollments';
20
20
  import type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners';
21
21
  import { type AutomationWorkflow, type WorkflowStep } from './workflow-steps';
22
+ import { type HostAction, type HostActionAlert, type HostActionStep } from '../model/host-actions';
22
23
  export interface ActionRunEnv {
23
24
  hostId: string;
24
25
  hostRef: FirebaseFirestore.DocumentReference;
@@ -32,20 +33,14 @@ export interface ActionRunEnv {
32
33
  */
33
34
  actionsAllowed: boolean;
34
35
  webhooksAllowed: boolean;
35
- /**
36
- * Whether the owning org holds the CRM suite (AGL-2611) — the gate the
37
- * five CRM steps take, resolved once beside `webhooksAllowed` and for the
38
- * same reason: a step gated on the org doc it already read costs no
39
- * second read per step.
40
- */
41
- crmAllowed: boolean;
42
36
  depth: number;
43
37
  /**
44
38
  * The owning org's billing doc, already read by the entitlement gate that
45
39
  * admitted this run, and the org's id beside it.
46
40
  *
47
41
  * Carried rather than re-read: both entry points resolve `getOrgForHost`
48
- * before they build this, so the dataset caps below cost no extra org read.
42
+ * before they build this, so a step that gates on the plan — another
43
+ * plugin's step, handed both — costs no extra org read.
49
44
  * Null only when the host has no resolvable org, which the gate treats as
50
45
  * the free plan.
51
46
  */
@@ -249,7 +244,7 @@ export declare function runEventActions(hostId: string, event: string, payload?:
249
244
  export type SingleActionSkip = 'missing' | 'disabled' | 'event' | 'conditions' | 'plan' | 'allowance' | 'failed';
250
245
  /** What one dispatched action did, for a caller that has to say so. */
251
246
  export interface SingleActionOutcome {
252
- /** Its steps ran, and the run counted on the site's meter. */
247
+ /** Its steps ran, and the run counted on the site's and workspace's meters. */
253
248
  ran: boolean;
254
249
  /** Why nothing ran; `null` when it ran. */
255
250
  skipped: SingleActionSkip | null;