shraga 0.1.45 → 0.1.46

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": "shraga",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "The teammate you delegate coding to — a self-hostable, multi-user AI coding agent web UI (Claude Code, with a pluggable engine seam).",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { hostname } from 'node:os';
3
4
  import { DATA_DIR } from './paths.ts';
4
5
  import { emitEvent } from './events/bus.ts';
5
6
  import { runTextQuery } from './sdk-utils.ts';
@@ -18,6 +19,15 @@ export class DataSyncOptions {
18
19
  * A real deployment must ALSO set DATA_SYNC_ENABLE=1. Dev/verify boots never sync/push.
19
20
  */
20
21
  enabled = process.env.DATA_SYNC_ENABLE === '1' || process.env.DATA_SYNC_ENABLE === 'true';
22
+ /**
23
+ * Owner-facing alerts are a PROD side effect, so only the designated single-writer instance
24
+ * (the same one that fires schedules) may DM owners. A dev laptop legitimately syncs the shared
25
+ * data repo, but it also shares .env's APP_NAME, OWNERS and the Slack bot token — so its alerts
26
+ * are indistinguishable from the box's. Past incident 2026-08-20: a laptop pinned to an older
27
+ * shraga re-sent a merge-conflict alert for a bug already fixed AND deployed on the box, and the
28
+ * DM gave no way to tell which instance sent it. Suppressed alerts are still logged locally.
29
+ */
30
+ notify = process.env.DATA_SYNC_SCHEDULER_ACTIVE === 'true';
21
31
  }
22
32
 
23
33
  export class DataSync {
@@ -550,6 +560,12 @@ export class DataSync {
550
560
  }
551
561
 
552
562
  private async notifyOwners(text: string): Promise<void> {
563
+ if (!this.options.notify) {
564
+ // Not the authoritative instance — log it so a dev run still surfaces the problem locally,
565
+ // but never DM owners (see DataSyncOptions.notify).
566
+ console.warn(`${TAG} notification suppressed (not the authoritative instance):\n${text}`);
567
+ return;
568
+ }
553
569
  // No Slack coupling here — resolve owner contacts and publish a deploy notice on the event bus.
554
570
  // The Slack feature (slackFeature) subscribes and DMs each owner. Owner resolution uses the
555
571
  // contacts store only, so data-sync stays transport-agnostic.
@@ -562,7 +578,10 @@ export class DataSync {
562
578
  console.warn(`${TAG} No owners (OWNERS env) with Slack IDs found, skipping notification`);
563
579
  return;
564
580
  }
565
- emitEvent('data-sync', { kind: 'deploy', owners, text });
581
+ // Stamp the sender: every owner alert must name the instance it came from, so "is this back?"
582
+ // is answerable without log archaeology across hosts.
583
+ const from = `${this.options.deploymentId || 'shraga'}@${hostname()}`;
584
+ emitEvent('data-sync', { kind: 'deploy', owners, text: `${text}\n\n_from ${from}_` });
566
585
  }
567
586
 
568
587
  /**