@trops/dash-core 0.1.641 → 0.1.643

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.
@@ -119,7 +119,7 @@ const WORKSPACE_DELETE$1 = "workspace-delete";
119
119
  const WORKSPACE_DELETE_COMPLETE = "workspace-delete-complete";
120
120
  const WORKSPACE_DELETE_ERROR = "workspace-delete-error";
121
121
 
122
- var workspaceEvents$1 = {
122
+ var workspaceEvents$2 = {
123
123
  WORKSPACE_LIST: WORKSPACE_LIST$1,
124
124
  WORKSPACE_LIST_COMPLETE,
125
125
  WORKSPACE_LIST_ERROR,
@@ -1027,7 +1027,7 @@ var botEvents$2 = {
1027
1027
  */
1028
1028
 
1029
1029
  const secureStorageEvents = secureStorageEvents$1;
1030
- const workspaceEvents = workspaceEvents$1;
1030
+ const workspaceEvents$1 = workspaceEvents$2;
1031
1031
  const layoutEvents = layoutEvents$1;
1032
1032
  const themeEvents = themeEvents$1;
1033
1033
  const dataEvents = dataEvents$1;
@@ -1089,14 +1089,14 @@ const API_GROUPS = {
1089
1089
  themes: [...Object.values(themeEvents), ...Object.values(themeFromUrlEvents)],
1090
1090
  websocket: Object.values(webSocketEvents),
1091
1091
  widgets: [],
1092
- workspace: Object.values(workspaceEvents),
1092
+ workspace: Object.values(workspaceEvents$1),
1093
1093
  };
1094
1094
 
1095
1095
  var events$8 = {
1096
1096
  public: publicEvents,
1097
1097
  API_GROUPS,
1098
1098
  ...secureStorageEvents,
1099
- ...workspaceEvents,
1099
+ ...workspaceEvents$1,
1100
1100
  ...layoutEvents,
1101
1101
  ...themeEvents,
1102
1102
  ...dataEvents,
@@ -1340,10 +1340,43 @@ var file = {
1340
1340
  checkDirectory: checkDirectory$1,
1341
1341
  };
1342
1342
 
1343
+ /**
1344
+ * workspaceEvents.js
1345
+ *
1346
+ * Main-process notifications about dashboards (workspaces). Today: "a
1347
+ * dashboard was deleted", so the Bot Factory can unassign + pause that
1348
+ * dashboard's team (bot-teams TEAM-001). Covers every delete path (app IPC
1349
+ * and the Dash MCP `delete_dashboard` tool) because both go through
1350
+ * workspaceController.deleteWorkspaceForApplication.
1351
+ *
1352
+ * Pure: no Electron.
1353
+ */
1354
+
1355
+ const deletedListeners = new Set();
1356
+
1357
+ /** @returns {() => void} unsubscribe */
1358
+ function onWorkspaceDeleted$1(fn) {
1359
+ deletedListeners.add(fn);
1360
+ return () => deletedListeners.delete(fn);
1361
+ }
1362
+
1363
+ function emitWorkspaceDeleted$1(workspaceId) {
1364
+ for (const fn of deletedListeners) {
1365
+ try {
1366
+ fn(workspaceId);
1367
+ } catch (_e) {
1368
+ // A listener failure must never fail the delete itself.
1369
+ }
1370
+ }
1371
+ }
1372
+
1373
+ var workspaceEvents = { onWorkspaceDeleted: onWorkspaceDeleted$1, emitWorkspaceDeleted: emitWorkspaceDeleted$1 };
1374
+
1343
1375
  const { app: app$g } = require$$0$2;
1344
1376
  const path$p = require$$1$2;
1345
1377
  const { writeFileSync: writeFileSync$2 } = require$$0$3;
1346
1378
  const { getFileContents: getFileContents$7 } = file;
1379
+ const { emitWorkspaceDeleted } = workspaceEvents;
1347
1380
 
1348
1381
  const configFilename$5 = "workspaces.json";
1349
1382
  const appName$7 = "Dashboard";
@@ -1500,6 +1533,11 @@ const workspaceController$3 = {
1500
1533
 
1501
1534
  writeFileSync$2(filename, JSON.stringify(filtered, null, 2));
1502
1535
 
1536
+ // Let the Bot Factory unassign + pause the deleted dashboard's team.
1537
+ if (filtered.length < workspacesArray.length) {
1538
+ emitWorkspaceDeleted(workspaceId);
1539
+ }
1540
+
1503
1541
  console.log(
1504
1542
  `[workspaceController] Workspace ${workspaceId} deleted successfully`,
1505
1543
  );
@@ -76684,10 +76722,11 @@ function matchSubscribedBots$1(bots, event, opts = {}) {
76684
76722
  );
76685
76723
  if (!subscribed) return false;
76686
76724
 
76687
- // Workspace scoping (forward-compatible): only enforced once events carry a
76688
- // workspaceId. An unscoped bot (no workspaceId) listens everywhere.
76725
+ // Team scoping: a bot on a dashboard's team only hears events stamped with
76726
+ // that dashboard. An unassigned bot (no workspaceId) listens everywhere.
76727
+ // Ids compare as strings (older saves may hold numeric ids).
76689
76728
  if (eventWorkspace && bot.workspaceId) {
76690
- return bot.workspaceId === eventWorkspace;
76729
+ return String(bot.workspaceId) === String(eventWorkspace);
76691
76730
  }
76692
76731
  return true;
76693
76732
  });
@@ -76880,6 +76919,56 @@ let BotEventPublisher$1 = class BotEventPublisher {
76880
76919
 
76881
76920
  var BotEventPublisher_1 = BotEventPublisher$1;
76882
76921
 
76922
+ /**
76923
+ * teams.js
76924
+ *
76925
+ * A dashboard's team is the set of bots whose `workspaceId` is that
76926
+ * dashboard (bot-teams PRD TEAM-001). Bots with no `workspaceId` are
76927
+ * unassigned and keep working as before.
76928
+ *
76929
+ * Pure (NFR-006): no Electron, no I/O — the store and pause controller are
76930
+ * passed in.
76931
+ */
76932
+
76933
+ /** Dashboard ids are compared as strings; empty → null (unassigned). */
76934
+ function normalizeWorkspaceId(id) {
76935
+ if (id === undefined || id === null || id === "") return null;
76936
+ return String(id);
76937
+ }
76938
+
76939
+ function isOnTeam(bot, workspaceId) {
76940
+ const want = normalizeWorkspaceId(workspaceId);
76941
+ return !!bot && normalizeWorkspaceId(bot.workspaceId) === want;
76942
+ }
76943
+
76944
+ /** A dashboard's bots; `null` lists the unassigned bots. */
76945
+ function teamOf(bots, workspaceId) {
76946
+ if (!Array.isArray(bots)) return [];
76947
+ return bots.filter((b) => isOnTeam(b, workspaceId));
76948
+ }
76949
+
76950
+ /**
76951
+ * A dashboard was deleted: its bots become unassigned AND paused — never
76952
+ * deleted, and never left running somewhere unexpected.
76953
+ * @returns {string[]} ids of the bots that were unassigned
76954
+ */
76955
+ function unassignTeam$1({ store, pause }, workspaceId) {
76956
+ if (normalizeWorkspaceId(workspaceId) === null) return [];
76957
+ const moved = [];
76958
+ for (const bot of teamOf(store.list(), workspaceId)) {
76959
+ try {
76960
+ store.update(bot.id, { workspaceId: null });
76961
+ pause.pauseBot(bot.id);
76962
+ moved.push(bot.id);
76963
+ } catch (_e) {
76964
+ // One bad record mustn't strand the rest of the team.
76965
+ }
76966
+ }
76967
+ return moved;
76968
+ }
76969
+
76970
+ var teams = { normalizeWorkspaceId, isOnTeam, teamOf, unassignTeam: unassignTeam$1 };
76971
+
76883
76972
  /**
76884
76973
  * rememberGrant.js — turn an "Always allow" bot approval into a durable grant,
76885
76974
  * and revoke it.
@@ -77017,6 +77106,8 @@ const { matchSubscribedBots } = eventMatcher;
77017
77106
  const { EventDispatcher } = EventDispatcher_1;
77018
77107
  const { normalizeMcpResult } = mcpResult;
77019
77108
  const BotEventPublisher = BotEventPublisher_1;
77109
+ const { unassignTeam } = teams;
77110
+ const { onWorkspaceDeleted } = workspaceEvents;
77020
77111
  const {
77021
77112
  checkChain,
77022
77113
  causeFromEvent,
@@ -77174,6 +77265,13 @@ const botController$1 = {
77174
77265
  for (const bot of bots)
77175
77266
  this._scheduler.catchUp(bot, this._lastRunAt(bot.id));
77176
77267
 
77268
+ // A deleted dashboard's team becomes unassigned + paused — never deleted,
77269
+ // never left running somewhere unexpected (bot-teams TEAM-001).
77270
+ if (this._offWorkspaceDeleted) this._offWorkspaceDeleted();
77271
+ this._offWorkspaceDeleted = onWorkspaceDeleted((workspaceId) => {
77272
+ unassignTeam({ store: this._store, pause: this._pause }, workspaceId);
77273
+ });
77274
+
77177
77275
  this._ready = true;
77178
77276
  },
77179
77277