@aglyn/plugins-workflows 1.0.0-beta.261 → 1.0.0-beta.262

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-workflows",
3
- "version": "1.0.0-beta.261",
3
+ "version": "1.0.0-beta.262",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,17 +25,17 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.261",
29
- "@aglyn/shared-data-mdi": "1.0.0-beta.261",
30
- "@aglyn/shared-ui-jsx": "1.0.0-beta.261",
31
- "@aglyn/shared-ui-next": "1.0.0-beta.261",
32
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.261",
33
- "@aglyn/shared-util-email": "1.0.0-beta.261",
34
- "@aglyn/shared-util-http": "1.0.0-beta.261",
35
- "@aglyn/shared-util-timestamp": "1.0.0-beta.261",
36
- "@aglyn/tenant-data-admin": "1.0.0-beta.261",
37
- "@aglyn/tenant-feature-instance": "1.0.0-beta.261",
38
- "@aglyn/tenant-runtime": "1.0.0-beta.261",
28
+ "@aglyn/aglyn": "1.0.0-beta.262",
29
+ "@aglyn/shared-data-mdi": "1.0.0-beta.262",
30
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.262",
31
+ "@aglyn/shared-ui-next": "1.0.0-beta.262",
32
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.262",
33
+ "@aglyn/shared-util-email": "1.0.0-beta.262",
34
+ "@aglyn/shared-util-http": "1.0.0-beta.262",
35
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.262",
36
+ "@aglyn/tenant-data-admin": "1.0.0-beta.262",
37
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.262",
38
+ "@aglyn/tenant-runtime": "1.0.0-beta.262",
39
39
  "@swc/helpers": "0.5.23"
40
40
  },
41
41
  "peerDependencies": {
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ import { PLUGIN_FIGURE_MAX_ROWS, pluginFigureChange, pluginFigureWindows, registerPluginFigureReader } from "@aglyn/aglyn/plugin-manager/plugin-figures";
17
+ import { figureCount, figurePlural, figureRow } from "@aglyn/aglyn/app-utils/weekly-summary";
17
18
  import { BUNDLE_ID } from "../constants/bundle-common.js";
18
19
  /** Runs of one result one window reads for the per-automation split. */ export const AUTOMATION_RUN_FIGURES_WINDOW_CEILING = 500;
19
20
  const WINDOWS = [
@@ -54,6 +55,18 @@ export function automationRunFigureReader(firestore) {
54
55
  description: 'Runs of the site’s automations — actions, workflows and organization automations — over the window: how many succeeded and failed, the failure rate, and the change in runs from the window before, in total and by automation.',
55
56
  scope: 'site',
56
57
  windows: WINDOWS,
58
+ weeklySummary: {
59
+ order: 110,
60
+ lines: (table)=>{
61
+ const all = figureRow(table, 'automation', 'All automations');
62
+ const succeeded = figureCount(all == null ? void 0 : all['succeeded']);
63
+ const failed = figureCount(all == null ? void 0 : all['failed']);
64
+ if (!succeeded && !failed) return [];
65
+ return [
66
+ `Automations: ${figurePlural(succeeded, 'run', 'runs')} finished` + (failed ? `, ${failed.toLocaleString('en-US')} failed` : '')
67
+ ];
68
+ }
69
+ },
57
70
  read: async (request)=>{
58
71
  if (!request.hostId) return {
59
72
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/server/automation-run-figures.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 {\n PLUGIN_FIGURE_MAX_ROWS,\n pluginFigureChange,\n pluginFigureWindows,\n registerPluginFigureReader,\n type PluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * A site's automation runs as a figure table another plugin reads by id\n * (AGL-3603) — the AI plugin's insights answer \"which automations failed?\"\n * from it.\n *\n * Every run an action, a workflow or an organization automation makes is a\n * row in the site's activity log carrying `result` (`succeeded` or `failed`)\n * and `target` — the same rows the run history lists. The totals are COUNT\n * aggregates over `result` in the window (the `activity` composite on\n * `result` + `createdAt`), so they are exact however busy the site is; the\n * per-automation split reads at most `AUTOMATION_RUN_FIGURES_WINDOW_CEILING`\n * runs of each result, projected to `target`, and says so when that read\n * low. A run's event payload names a person and is never read.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\n/** Runs of one result one window reads for the per-automation split. */\nexport const AUTOMATION_RUN_FIGURES_WINDOW_CEILING = 500\n\nconst WINDOWS: readonly number[] = [7, 14, 30, 90]\n\ninterface AutomationCounts {\n succeeded: number\n failed: number\n}\n\nfunction runsOf(\n firestore: Firestore,\n hostId: string,\n result: 'succeeded' | 'failed',\n startMs: number,\n endMs: number,\n): FirebaseFirestore.Query {\n return firestore\n .collection('hosts')\n .doc(hostId)\n .collection('activity')\n .where('result', '==', result)\n .where('createdAt', '>=', new Date(startMs))\n .where('createdAt', '<', new Date(endMs))\n}\n\nasync function countOf(query: FirebaseFirestore.Query): Promise<number> {\n const snapshot = await query.count().get()\n return Number(snapshot.data().count) || 0\n}\n\n/** Runs of one result in the window, by the automation they belong to, read to the ceiling. */\nasync function byAutomation(\n query: FirebaseFirestore.Query,\n): Promise<{ names: Map<string, number>; truncated: boolean }> {\n const snapshot = await query\n .orderBy('createdAt', 'desc')\n .select('target')\n .limit(AUTOMATION_RUN_FIGURES_WINDOW_CEILING + 1)\n .get()\n const names = new Map<string, number>()\n for (const doc of snapshot.docs.slice(0, AUTOMATION_RUN_FIGURES_WINDOW_CEILING)) {\n const target = (doc.get('target') ?? {}) as { name?: unknown; id?: unknown }\n const name =\n (typeof target.name === 'string' && target.name.trim()) || 'An automation with no name'\n names.set(name, (names.get(name) ?? 0) + 1)\n }\n return { names, truncated: snapshot.docs.length > AUTOMATION_RUN_FIGURES_WINDOW_CEILING }\n}\n\nconst failureRate = (counts: AutomationCounts): number | null => {\n const total = counts.succeeded + counts.failed\n return total > 0 ? Math.round((counts.failed / total) * 1_000) / 10 : null\n}\n\nexport function automationRunFigureReader(firestore: () => Firestore): PluginFigureReader {\n return {\n id: 'automations.runs',\n label: 'Automation runs',\n description:\n 'Runs of the site’s automations — actions, workflows and organization automations — over the window: how many succeeded and failed, the failure rate, and the change in runs from the window before, in total and by automation.',\n scope: 'site',\n windows: WINDOWS,\n read: async (request) => {\n if (!request.hostId) return { ok: false, status: 400, error: 'Open a site to read its automation runs' }\n if (!WINDOWS.includes(request.days)) {\n return { ok: false, status: 400, error: 'That window is not one automation runs are read over' }\n }\n const db = firestore()\n const hostId = request.hostId\n const { current, previous } = pluginFigureWindows(request.now, request.days)\n const [succeeded, failed, before, okRuns, failedRuns] = await Promise.all([\n countOf(runsOf(db, hostId, 'succeeded', current.startMs, current.endMs)),\n countOf(runsOf(db, hostId, 'failed', current.startMs, current.endMs)),\n Promise.all([\n countOf(runsOf(db, hostId, 'succeeded', previous.startMs, previous.endMs)),\n countOf(runsOf(db, hostId, 'failed', previous.startMs, previous.endMs)),\n ]).then(([a, b]) => a + b),\n byAutomation(runsOf(db, hostId, 'succeeded', current.startMs, current.endMs)),\n byAutomation(runsOf(db, hostId, 'failed', current.startMs, current.endMs)),\n ])\n const automations = new Map<string, AutomationCounts>()\n for (const [name, count] of okRuns.names) {\n automations.set(name, { succeeded: count, failed: automations.get(name)?.failed ?? 0 })\n }\n for (const [name, count] of failedRuns.names) {\n automations.set(name, { succeeded: automations.get(name)?.succeeded ?? 0, failed: count })\n }\n // The most failures first: that is the question a person asks of runs.\n const ranked = [...automations.entries()].sort(\n (a, b) => b[1].failed - a[1].failed || b[1].succeeded - a[1].succeeded || a[0].localeCompare(b[0]),\n )\n const totals = { succeeded, failed }\n return {\n ok: true,\n table: {\n title: 'Automation runs',\n source: { label: 'Automation', path: 'automation' },\n period: { from: current.from, to: current.to, days: request.days },\n columns: [\n { key: 'automation', label: 'Automation', kind: 'text' },\n { key: 'succeeded', label: 'Succeeded', kind: 'count' },\n { key: 'failed', label: 'Failed', kind: 'count' },\n { key: 'failureRate', label: 'Failure rate', kind: 'percent' },\n { key: 'change', label: 'Change in runs', kind: 'change' },\n ],\n rows: [\n {\n automation: 'All automations',\n succeeded,\n failed,\n failureRate: failureRate(totals),\n change: pluginFigureChange(succeeded + failed, before),\n },\n ...ranked.slice(0, PLUGIN_FIGURE_MAX_ROWS - 1).map(([automation, counts]) => ({\n automation,\n succeeded: counts.succeeded,\n failed: counts.failed,\n failureRate: failureRate(counts),\n change: null,\n })),\n ],\n omitted: Math.max(0, ranked.length - (PLUGIN_FIGURE_MAX_ROWS - 1)),\n notes: [\n 'A run is counted on the day it ran. A run waiting on a later step counts as succeeded so far.',\n ...(okRuns.truncated || failedRuns.truncated\n ? [\n `The split by automation counts the most recent ${AUTOMATION_RUN_FIGURES_WINDOW_CEILING.toLocaleString('en-US')} runs of each result; the All automations row counts every run.`,\n ]\n : []),\n ],\n },\n }\n },\n }\n}\n\n/** Registers the automation runs reader from the console surface, where insight jobs run. */\nexport function registerAutomationRunFigureReader(firestore: () => Firestore): void {\n registerPluginFigureReader(automationRunFigureReader(firestore), { pluginId: BUNDLE_ID })\n}\n"],"names":["PLUGIN_FIGURE_MAX_ROWS","pluginFigureChange","pluginFigureWindows","registerPluginFigureReader","BUNDLE_ID","AUTOMATION_RUN_FIGURES_WINDOW_CEILING","WINDOWS","runsOf","firestore","hostId","result","startMs","endMs","collection","doc","where","Date","countOf","query","snapshot","count","get","Number","data","byAutomation","orderBy","select","limit","names","Map","docs","slice","target","name","trim","set","truncated","length","failureRate","counts","total","succeeded","failed","Math","round","automationRunFigureReader","id","label","description","scope","windows","read","request","ok","status","error","includes","days","db","current","previous","now","before","okRuns","failedRuns","Promise","all","then","a","b","automations","ranked","entries","sort","localeCompare","totals","table","title","source","path","period","from","to","columns","key","kind","rows","automation","change","map","omitted","max","notes","toLocaleString","registerAutomationRunFigureReader","pluginId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/server/automation-run-figures.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 {\n PLUGIN_FIGURE_MAX_ROWS,\n pluginFigureChange,\n pluginFigureWindows,\n registerPluginFigureReader,\n type PluginFigureReader,\n} from '@aglyn/aglyn/plugin-manager/plugin-figures'\nimport { figureCount, figurePlural, figureRow } from '@aglyn/aglyn/app-utils/weekly-summary'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * A site's automation runs as a figure table another plugin reads by id\n * (AGL-3603) — the AI plugin's insights answer \"which automations failed?\"\n * from it.\n *\n * Every run an action, a workflow or an organization automation makes is a\n * row in the site's activity log carrying `result` (`succeeded` or `failed`)\n * and `target` — the same rows the run history lists. The totals are COUNT\n * aggregates over `result` in the window (the `activity` composite on\n * `result` + `createdAt`), so they are exact however busy the site is; the\n * per-automation split reads at most `AUTOMATION_RUN_FIGURES_WINDOW_CEILING`\n * runs of each result, projected to `target`, and says so when that read\n * low. A run's event payload names a person and is never read.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\n/** Runs of one result one window reads for the per-automation split. */\nexport const AUTOMATION_RUN_FIGURES_WINDOW_CEILING = 500\n\nconst WINDOWS: readonly number[] = [7, 14, 30, 90]\n\ninterface AutomationCounts {\n succeeded: number\n failed: number\n}\n\nfunction runsOf(\n firestore: Firestore,\n hostId: string,\n result: 'succeeded' | 'failed',\n startMs: number,\n endMs: number,\n): FirebaseFirestore.Query {\n return firestore\n .collection('hosts')\n .doc(hostId)\n .collection('activity')\n .where('result', '==', result)\n .where('createdAt', '>=', new Date(startMs))\n .where('createdAt', '<', new Date(endMs))\n}\n\nasync function countOf(query: FirebaseFirestore.Query): Promise<number> {\n const snapshot = await query.count().get()\n return Number(snapshot.data().count) || 0\n}\n\n/** Runs of one result in the window, by the automation they belong to, read to the ceiling. */\nasync function byAutomation(\n query: FirebaseFirestore.Query,\n): Promise<{ names: Map<string, number>; truncated: boolean }> {\n const snapshot = await query\n .orderBy('createdAt', 'desc')\n .select('target')\n .limit(AUTOMATION_RUN_FIGURES_WINDOW_CEILING + 1)\n .get()\n const names = new Map<string, number>()\n for (const doc of snapshot.docs.slice(0, AUTOMATION_RUN_FIGURES_WINDOW_CEILING)) {\n const target = (doc.get('target') ?? {}) as { name?: unknown; id?: unknown }\n const name =\n (typeof target.name === 'string' && target.name.trim()) || 'An automation with no name'\n names.set(name, (names.get(name) ?? 0) + 1)\n }\n return { names, truncated: snapshot.docs.length > AUTOMATION_RUN_FIGURES_WINDOW_CEILING }\n}\n\nconst failureRate = (counts: AutomationCounts): number | null => {\n const total = counts.succeeded + counts.failed\n return total > 0 ? Math.round((counts.failed / total) * 1_000) / 10 : null\n}\n\nexport function automationRunFigureReader(firestore: () => Firestore): PluginFigureReader {\n return {\n id: 'automations.runs',\n label: 'Automation runs',\n description:\n 'Runs of the site’s automations — actions, workflows and organization automations — over the window: how many succeeded and failed, the failure rate, and the change in runs from the window before, in total and by automation.',\n scope: 'site',\n windows: WINDOWS,\n weeklySummary: {\n order: 110,\n lines: (table) => {\n const all = figureRow(table, 'automation', 'All automations')\n const succeeded = figureCount(all?.['succeeded'])\n const failed = figureCount(all?.['failed'])\n if (!succeeded && !failed) return []\n return [\n `Automations: ${figurePlural(succeeded, 'run', 'runs')} finished` +\n (failed ? `, ${failed.toLocaleString('en-US')} failed` : ''),\n ]\n },\n },\n read: async (request) => {\n if (!request.hostId) return { ok: false, status: 400, error: 'Open a site to read its automation runs' }\n if (!WINDOWS.includes(request.days)) {\n return { ok: false, status: 400, error: 'That window is not one automation runs are read over' }\n }\n const db = firestore()\n const hostId = request.hostId\n const { current, previous } = pluginFigureWindows(request.now, request.days)\n const [succeeded, failed, before, okRuns, failedRuns] = await Promise.all([\n countOf(runsOf(db, hostId, 'succeeded', current.startMs, current.endMs)),\n countOf(runsOf(db, hostId, 'failed', current.startMs, current.endMs)),\n Promise.all([\n countOf(runsOf(db, hostId, 'succeeded', previous.startMs, previous.endMs)),\n countOf(runsOf(db, hostId, 'failed', previous.startMs, previous.endMs)),\n ]).then(([a, b]) => a + b),\n byAutomation(runsOf(db, hostId, 'succeeded', current.startMs, current.endMs)),\n byAutomation(runsOf(db, hostId, 'failed', current.startMs, current.endMs)),\n ])\n const automations = new Map<string, AutomationCounts>()\n for (const [name, count] of okRuns.names) {\n automations.set(name, { succeeded: count, failed: automations.get(name)?.failed ?? 0 })\n }\n for (const [name, count] of failedRuns.names) {\n automations.set(name, { succeeded: automations.get(name)?.succeeded ?? 0, failed: count })\n }\n // The most failures first: that is the question a person asks of runs.\n const ranked = [...automations.entries()].sort(\n (a, b) => b[1].failed - a[1].failed || b[1].succeeded - a[1].succeeded || a[0].localeCompare(b[0]),\n )\n const totals = { succeeded, failed }\n return {\n ok: true,\n table: {\n title: 'Automation runs',\n source: { label: 'Automation', path: 'automation' },\n period: { from: current.from, to: current.to, days: request.days },\n columns: [\n { key: 'automation', label: 'Automation', kind: 'text' },\n { key: 'succeeded', label: 'Succeeded', kind: 'count' },\n { key: 'failed', label: 'Failed', kind: 'count' },\n { key: 'failureRate', label: 'Failure rate', kind: 'percent' },\n { key: 'change', label: 'Change in runs', kind: 'change' },\n ],\n rows: [\n {\n automation: 'All automations',\n succeeded,\n failed,\n failureRate: failureRate(totals),\n change: pluginFigureChange(succeeded + failed, before),\n },\n ...ranked.slice(0, PLUGIN_FIGURE_MAX_ROWS - 1).map(([automation, counts]) => ({\n automation,\n succeeded: counts.succeeded,\n failed: counts.failed,\n failureRate: failureRate(counts),\n change: null,\n })),\n ],\n omitted: Math.max(0, ranked.length - (PLUGIN_FIGURE_MAX_ROWS - 1)),\n notes: [\n 'A run is counted on the day it ran. A run waiting on a later step counts as succeeded so far.',\n ...(okRuns.truncated || failedRuns.truncated\n ? [\n `The split by automation counts the most recent ${AUTOMATION_RUN_FIGURES_WINDOW_CEILING.toLocaleString('en-US')} runs of each result; the All automations row counts every run.`,\n ]\n : []),\n ],\n },\n }\n },\n }\n}\n\n/** Registers the automation runs reader from the console surface, where insight jobs run. */\nexport function registerAutomationRunFigureReader(firestore: () => Firestore): void {\n registerPluginFigureReader(automationRunFigureReader(firestore), { pluginId: BUNDLE_ID })\n}\n"],"names":["PLUGIN_FIGURE_MAX_ROWS","pluginFigureChange","pluginFigureWindows","registerPluginFigureReader","figureCount","figurePlural","figureRow","BUNDLE_ID","AUTOMATION_RUN_FIGURES_WINDOW_CEILING","WINDOWS","runsOf","firestore","hostId","result","startMs","endMs","collection","doc","where","Date","countOf","query","snapshot","count","get","Number","data","byAutomation","orderBy","select","limit","names","Map","docs","slice","target","name","trim","set","truncated","length","failureRate","counts","total","succeeded","failed","Math","round","automationRunFigureReader","id","label","description","scope","windows","weeklySummary","order","lines","table","all","toLocaleString","read","request","ok","status","error","includes","days","db","current","previous","now","before","okRuns","failedRuns","Promise","then","a","b","automations","ranked","entries","sort","localeCompare","totals","title","source","path","period","from","to","columns","key","kind","rows","automation","change","map","omitted","max","notes","registerAutomationRunFigureReader","pluginId"],"mappings":"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"}