@runeya/runeya 2.0.121 → 2.0.123

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.
@@ -17,7 +17,6 @@ import {
17
17
  apiKeyStore,
18
18
  appVersion,
19
19
  applyPersistedLanMode,
20
- atomicWriteSecure,
21
20
  auditService,
22
21
  canEnvAction,
23
22
  chatRouter,
@@ -51,7 +50,6 @@ import {
51
50
  requireServiceAction,
52
51
  resolveAgentCwd,
53
52
  resolveConversationCwd,
54
- resolveConversationRunDir,
55
53
  resolveEffectiveAgentId,
56
54
  resolveServiceVariables,
57
55
  resolveWorkspaceCwd,
@@ -64,7 +62,7 @@ import {
64
62
  soleAccountScope,
65
63
  startCliSession,
66
64
  vaultOfProject
67
- } from "./chunk-HJUFIZ7H.js";
65
+ } from "./chunk-DUHCY3JU.js";
68
66
  import {
69
67
  resolveCliInvocation,
70
68
  whichCommand
@@ -87,7 +85,7 @@ import {
87
85
  stopClaudeTranscriptWatcher,
88
86
  stopCodexTranscriptWatcher,
89
87
  verifyJwt
90
- } from "./chunk-OVV52N5F.js";
88
+ } from "./chunk-TNJFDWJX.js";
91
89
  import "./chunk-MD3VW4HQ.js";
92
90
  import {
93
91
  agentManager,
@@ -125,11 +123,15 @@ import {
125
123
  stableEncryptValue,
126
124
  traefikManager,
127
125
  unregisterInstance
128
- } from "./chunk-CNTL6NVU.js";
126
+ } from "./chunk-MSLFR2EN.js";
129
127
  import {
130
128
  settingsManager
131
- } from "./chunk-3N55WR7F.js";
132
- import "./chunk-YZU5FNXR.js";
129
+ } from "./chunk-N3ZI35SC.js";
130
+ import {
131
+ atomicWriteSecure,
132
+ resolveConversationRunDir,
133
+ sweepStaleTempFiles
134
+ } from "./chunk-HK5QNU6P.js";
133
135
  import {
134
136
  AGENT_LONG_FETCH_TIMEOUT,
135
137
  AGENT_STOP_FETCH_TIMEOUT,
@@ -165,6 +167,7 @@ import {
165
167
  writeEntity
166
168
  } from "./chunk-T2BV5WZE.js";
167
169
  import {
170
+ allVaultRoots,
168
171
  env,
169
172
  forgetOrgLogo,
170
173
  keyRoot,
@@ -6579,11 +6582,62 @@ async function reconcileOrgCanary(orgId) {
6579
6582
  }
6580
6583
 
6581
6584
  // ../server/src/services/cloud-sync-bridge.ts
6582
- import { randomUUID as randomUUID5 } from "crypto";
6585
+ import { randomUUID as randomUUID6 } from "crypto";
6583
6586
  import { mkdir as mkdir7, rm as rm3 } from "fs/promises";
6587
+ import * as Y2 from "yjs";
6588
+
6589
+ // ../server/src/services/sync-doc-cache.ts
6590
+ import { readFileSync, renameSync, rmSync, writeFileSync, mkdirSync } from "fs";
6591
+ import { readFile as readFile4 } from "fs/promises";
6592
+ import { dirname as dirname4, join as join10 } from "path";
6593
+ import { randomUUID as randomUUID5 } from "crypto";
6584
6594
  import * as Y from "yjs";
6595
+ var FILE = "sync-doc.bin";
6596
+ function cachePath(orgId) {
6597
+ return join10(orgVaultRoot(orgId), FILE);
6598
+ }
6599
+ async function readSyncDoc(orgId) {
6600
+ try {
6601
+ return new Uint8Array(await readFile4(cachePath(orgId)));
6602
+ } catch {
6603
+ return null;
6604
+ }
6605
+ }
6606
+ function mergeWith(existing, state) {
6607
+ if (!existing) return state;
6608
+ try {
6609
+ return Y.mergeUpdates([existing, state]);
6610
+ } catch {
6611
+ return state;
6612
+ }
6613
+ }
6614
+ async function writeSyncDoc(orgId, state) {
6615
+ await atomicWriteSecure(cachePath(orgId), mergeWith(await readSyncDoc(orgId), state));
6616
+ }
6617
+ function writeSyncDocSync(orgId, state) {
6618
+ const path = cachePath(orgId);
6619
+ let existing = null;
6620
+ try {
6621
+ existing = new Uint8Array(readFileSync(path));
6622
+ } catch {
6623
+ }
6624
+ mkdirSync(dirname4(path), { recursive: true });
6625
+ const tmp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
6626
+ try {
6627
+ writeFileSync(tmp, mergeWith(existing, state), { mode: 384 });
6628
+ renameSync(tmp, path);
6629
+ } catch (err) {
6630
+ rmSync(tmp, { force: true });
6631
+ throw err;
6632
+ }
6633
+ }
6634
+
6635
+ // ../server/src/services/cloud-sync-bridge.ts
6585
6636
  var LOCAL_ORIGIN = "local-bridge";
6586
6637
  var REMOTE_ORIGIN = "remote";
6638
+ var CACHE_ORIGIN = "cache";
6639
+ var RESTORED_PROJECTS = "projectRestores";
6640
+ var PERSIST_DEBOUNCE_MS = 1e3;
6587
6641
  var RECONNECT_DELAY_MS = 3e3;
6588
6642
  var UNAUTHENTICATED_RETRY_MS = 5 * 6e4;
6589
6643
  var OUTDATED_RETRY_MS = 30 * 6e4;
@@ -6616,12 +6670,12 @@ var ProjectHeldElsewhereError = class extends Error {
6616
6670
  code = "PROJECT_HELD_ELSEWHERE";
6617
6671
  };
6618
6672
  var OrgSyncConnection = class {
6619
- doc = new Y.Doc();
6673
+ doc = new Y2.Doc();
6620
6674
  projects = getCollection(this.doc, "projects");
6621
6675
  services = getCollection(this.doc, "services");
6622
6676
  environments = getCollection(this.doc, "environments");
6623
6677
  logParsers = getCollection(this.doc, PARSERS);
6624
- conn = randomUUID5();
6678
+ conn = randomUUID6();
6625
6679
  link;
6626
6680
  stopped = false;
6627
6681
  initializing = true;
@@ -6657,6 +6711,39 @@ var OrgSyncConnection = class {
6657
6711
  * creating a project sees it appear on their teammates' machines.
6658
6712
  */
6659
6713
  initialDocApplied = false;
6714
+ /**
6715
+ * Whether local state has been written into the doc.
6716
+ *
6717
+ * Never before the doc holds the known history — reloaded from disk, or
6718
+ * received from the cloud. Written into an empty doc, local state is
6719
+ * concurrent with every past write and loses to some of them at random: an
6720
+ * offline edit reverted, a service on disk erased by an older deletion.
6721
+ */
6722
+ seeded = false;
6723
+ markOpened;
6724
+ /**
6725
+ * Réglée quand le document tient l'état d'ouverture du cloud — ou quand il ne
6726
+ * l'aura pas : session refusée, app trop ancienne, connexion arrêtée.
6727
+ */
6728
+ opened = new Promise((resolve4) => {
6729
+ this.markOpened = resolve4;
6730
+ });
6731
+ /** Store applications started by remote events and not finished yet. */
6732
+ applying = /* @__PURE__ */ new Set();
6733
+ /**
6734
+ * When each entity was last changed by a remote update (`collection:id` → tick).
6735
+ *
6736
+ * Reconciliation reads the stores, waits, then writes what it read. A remote
6737
+ * change landing in between makes that read stale, and writing it would erase
6738
+ * the value that just arrived — for every member. Entities touched since the
6739
+ * read began are left alone: their application brings the disk up to date,
6740
+ * and the reconciliation it triggers starts from there.
6741
+ */
6742
+ remoteTouch = /* @__PURE__ */ new Map();
6743
+ remoteClock = 0;
6744
+ persistTimer = null;
6745
+ /** The cache write under way, so stopping can wait for it to land. */
6746
+ persisting = Promise.resolve();
6660
6747
  unsubscribes = [];
6661
6748
  onSaved = () => void this.reconcileLocalToDoc();
6662
6749
  constructor(link) {
@@ -6686,29 +6773,81 @@ var OrgSyncConnection = class {
6686
6773
  if (txn.origin !== REMOTE_ORIGIN) return;
6687
6774
  const isInitial = !this.initialDocApplied;
6688
6775
  if (name === "projects") this.initialDocApplied = true;
6689
- void this.applyDocEventsToStore(name, events, isInitial);
6776
+ this.track(this.applyDocEventsToStore(name, events, isInitial));
6690
6777
  });
6691
6778
  }
6692
6779
  this.logParsers.observeDeep((events, txn) => {
6693
6780
  if (txn.origin !== REMOTE_ORIGIN) return;
6694
- void this.applyParserEventsToStore(events);
6781
+ this.track(this.applyParserEventsToStore(events));
6695
6782
  });
6696
6783
  projectStore.events.on("saved", this.onSaved);
6697
6784
  this.unsubscribes.push(serviceStore.onChange(this.onSaved));
6698
6785
  this.unsubscribes.push(environmentStore.onChange(this.onSaved));
6699
6786
  this.unsubscribes.push(logParserStore.onLocalChange(this.onSaved));
6787
+ const cached = await readSyncDoc(this.link.orgId);
6788
+ let cacheLoaded = false;
6789
+ if (cached) {
6790
+ try {
6791
+ Y2.applyUpdate(this.doc, cached, CACHE_ORIGIN);
6792
+ cacheLoaded = true;
6793
+ } catch (err) {
6794
+ console.error("[cloud-sync] unreadable sync cache for org %s, ignored:", this.link.orgId, err.message);
6795
+ }
6796
+ }
6797
+ this.doc.on("update", () => this.schedulePersist());
6798
+ if (cacheLoaded) await this.seed();
6799
+ void this.streamLoop();
6800
+ }
6801
+ /** Write local state on top of the known history, then owe it to the cloud. */
6802
+ async seed() {
6803
+ this.seeded = true;
6700
6804
  await this.reconcileLocalToDoc();
6701
6805
  this.initializing = false;
6702
6806
  this.needsPush = true;
6703
- void this.streamLoop();
6704
6807
  }
6808
+ track(work) {
6809
+ this.applying.add(work);
6810
+ void work.finally(() => this.applying.delete(work));
6811
+ }
6812
+ async settleApplying() {
6813
+ while (this.applying.size > 0) await Promise.allSettled(this.applying);
6814
+ }
6815
+ schedulePersist() {
6816
+ if (this.stopped || this.persistTimer) return;
6817
+ this.persistTimer = setTimeout(() => {
6818
+ this.persistTimer = null;
6819
+ this.persisting = this.persisting.then(() => this.persist());
6820
+ }, PERSIST_DEBOUNCE_MS);
6821
+ }
6822
+ async persist() {
6823
+ if (this.stopped) return;
6824
+ try {
6825
+ await writeSyncDoc(this.link.orgId, Y2.encodeStateAsUpdate(this.doc));
6826
+ } catch (err) {
6827
+ console.error("[cloud-sync] could not save sync cache for org %s:", this.link.orgId, err.message);
6828
+ }
6829
+ }
6830
+ /** Resolves once any cache write already under way has landed. */
6705
6831
  stop() {
6706
6832
  this.stopped = true;
6833
+ this.markOpened();
6707
6834
  this.state = "stopped";
6708
6835
  projectStore.events.off("saved", this.onSaved);
6709
6836
  for (const off of this.unsubscribes.splice(0)) off();
6710
6837
  this.abort?.abort();
6838
+ if (this.persistTimer) {
6839
+ clearTimeout(this.persistTimer);
6840
+ this.persistTimer = null;
6841
+ }
6842
+ if (this.seeded) {
6843
+ try {
6844
+ writeSyncDocSync(this.link.orgId, Y2.encodeStateAsUpdate(this.doc));
6845
+ } catch (err) {
6846
+ console.error("[cloud-sync] could not save sync cache for org %s:", this.link.orgId, err.message);
6847
+ }
6848
+ }
6711
6849
  this.doc.destroy();
6850
+ return this.persisting;
6712
6851
  }
6713
6852
  // ---- Transport -------------------------------------------------------
6714
6853
  /**
@@ -6745,6 +6884,7 @@ var OrgSyncConnection = class {
6745
6884
  }
6746
6885
  /** Remember the floor and stop pretending sync will resume on its own. */
6747
6886
  markOutdated(err) {
6887
+ this.markOpened();
6748
6888
  const alreadyReported = this.state === "outdated";
6749
6889
  this.state = "outdated";
6750
6890
  this.lastIncompatibleVersion = err.lastIncompatibleVersion;
@@ -6757,6 +6897,7 @@ var OrgSyncConnection = class {
6757
6897
  * every slow retry: the point of retrying is to recover quietly.
6758
6898
  */
6759
6899
  markUnauthenticated(err) {
6900
+ this.markOpened();
6760
6901
  const alreadyReported = this.state === "unauthenticated";
6761
6902
  this.state = "unauthenticated";
6762
6903
  this.needsPush = true;
@@ -6786,7 +6927,7 @@ var OrgSyncConnection = class {
6786
6927
  if (!svRes.ok) return;
6787
6928
  const { sv } = await svRes.json();
6788
6929
  const cloudSv = new Uint8Array(Buffer.from(sv, "base64"));
6789
- const diff = Y.encodeStateAsUpdate(this.doc, cloudSv);
6930
+ const diff = Y2.encodeStateAsUpdate(this.doc, cloudSv);
6790
6931
  await this.pushUpdate(diff);
6791
6932
  } catch (err) {
6792
6933
  if (err instanceof SyncAuthError || err instanceof SyncOutdatedError) throw err;
@@ -6848,7 +6989,7 @@ var OrgSyncConnection = class {
6848
6989
  }
6849
6990
  async streamOnce() {
6850
6991
  this.abort = new AbortController();
6851
- const since = Buffer.from(Y.encodeStateVector(this.doc)).toString("base64");
6992
+ const since = Buffer.from(Y2.encodeStateVector(this.doc)).toString("base64");
6852
6993
  const res = await fetch(this.url(`/stream?conn=${this.conn}&since=${encodeURIComponent(since)}`), {
6853
6994
  headers: { Authorization: await this.authHeader(), [APP_VERSION_HEADER]: appVersion },
6854
6995
  signal: this.abort.signal
@@ -6872,8 +7013,18 @@ var OrgSyncConnection = class {
6872
7013
  try {
6873
7014
  const payload = JSON.parse(line.slice(6));
6874
7015
  const update = new Uint8Array(Buffer.from(payload.update, "base64"));
6875
- Y.applyUpdate(this.doc, update, REMOTE_ORIGIN);
7016
+ Y2.applyUpdate(this.doc, update, REMOTE_ORIGIN);
6876
7017
  } catch {
7018
+ continue;
7019
+ }
7020
+ this.initialDocApplied = true;
7021
+ this.markOpened();
7022
+ if (!this.seeded) {
7023
+ await this.settleApplying();
7024
+ if (this.stopped) return;
7025
+ await this.seed();
7026
+ await this.initialPush();
7027
+ this.needsPush = false;
6877
7028
  }
6878
7029
  }
6879
7030
  }
@@ -6996,6 +7147,7 @@ var OrgSyncConnection = class {
6996
7147
  this.pulling = false;
6997
7148
  }
6998
7149
  await serviceStore.relocateAll();
7150
+ await environmentStore.relocateAll();
6999
7151
  await this.reconcileLocalToDoc();
7000
7152
  }
7001
7153
  // ---- Local ↔ Doc reconciliation --------------------------------------
@@ -7018,6 +7170,7 @@ var OrgSyncConnection = class {
7018
7170
  if (typeof topKey === "string") affected.add(topKey);
7019
7171
  }
7020
7172
  }
7173
+ for (const id of [...deleted, ...affected]) this.remoteTouch.set(`${name}:${id}`, ++this.remoteClock);
7021
7174
  for (const id of deleted) {
7022
7175
  if (!this.isTrackedForDeletion(name, id)) continue;
7023
7176
  await this.deleteLocally(name, id);
@@ -7025,6 +7178,7 @@ var OrgSyncConnection = class {
7025
7178
  if (name === "projects" && !isInitial) {
7026
7179
  for (const id of added) {
7027
7180
  if (deleted.has(id) || this.tracked.has(id)) continue;
7181
+ if (this.doc.getMap(RESTORED_PROJECTS).has(id)) continue;
7028
7182
  try {
7029
7183
  await this.followProject(id);
7030
7184
  } catch (err) {
@@ -7170,7 +7324,9 @@ var OrgSyncConnection = class {
7170
7324
  * propagate — otherwise a service could never be removed.
7171
7325
  */
7172
7326
  async reconcileLocalToDoc() {
7173
- if (this.stopped || this.pulling) return;
7327
+ if (this.stopped || this.pulling || !this.seeded) return;
7328
+ const readAt = this.remoteClock;
7329
+ const touchedSinceRead = (name, id) => (this.remoteTouch.get(`${name}:${id}`) ?? 0) > readAt;
7174
7330
  const projects = (await projectStore.listDiskFormByOrg(this.link.orgId)).filter((p) => !this.refusalFor(p.id));
7175
7331
  const projectIds = projects.map((p) => p.id);
7176
7332
  const serviceIds = [...new Set(projects.flatMap((p) => p.serviceIds ?? []))];
@@ -7188,10 +7344,12 @@ var OrgSyncConnection = class {
7188
7344
  const collection = this.collection(name);
7189
7345
  const entities = local[name];
7190
7346
  const ids = new Set(entities.map((e) => e.id));
7191
- for (const entity of entities) writeEntity(collection, entity.id, entity);
7347
+ for (const entity of entities) {
7348
+ if (!touchedSinceRead(name, entity.id)) writeEntity(collection, entity.id, entity);
7349
+ }
7192
7350
  if (name === "projects") continue;
7193
7351
  for (const id of Array.from(collection.keys())) {
7194
- if (ids.has(id)) continue;
7352
+ if (ids.has(id) || touchedSinceRead(name, id)) continue;
7195
7353
  if (this.belongsToOwnedProject(name, id, ownedProjectIds)) collection.delete(id);
7196
7354
  }
7197
7355
  }
@@ -7380,21 +7538,43 @@ var CloudSyncBridge = class {
7380
7538
  disconnect(orgId) {
7381
7539
  const existing = this.connections.get(orgId);
7382
7540
  if (existing) {
7383
- existing.stop();
7541
+ void existing.stop();
7384
7542
  this.connections.delete(orgId);
7385
7543
  }
7386
7544
  }
7545
+ /**
7546
+ * Attendre que le document d'une organisation tienne l'état du cloud.
7547
+ *
7548
+ * Suivre un projet juste après avoir ouvert la connexion ne trouvait rien : la
7549
+ * trame d'ouverture n'était pas arrivée, et l'app concluait à un échec. Rend la
7550
+ * main au plus tard après `timeoutMs` — l'appelant lit alors l'état.
7551
+ */
7552
+ async whenOpened(orgId, timeoutMs) {
7553
+ const conn = this.connections.get(orgId);
7554
+ if (!conn) return;
7555
+ let timer;
7556
+ await Promise.race([
7557
+ conn.opened,
7558
+ new Promise((resolve4) => {
7559
+ timer = setTimeout(resolve4, timeoutMs);
7560
+ })
7561
+ ]);
7562
+ clearTimeout(timer);
7563
+ }
7564
+ /** Stops at once; the promise only says when pending cache writes have landed. */
7387
7565
  stopAll() {
7388
- for (const conn of this.connections.values()) conn.stop();
7566
+ const stopping = [...this.connections.values()].map((conn) => conn.stop());
7389
7567
  this.connections.clear();
7390
7568
  this.keyMismatch.clear();
7569
+ return Promise.all(stopping).then(() => {
7570
+ });
7391
7571
  }
7392
7572
  };
7393
7573
  var cloudSyncBridge = new CloudSyncBridge();
7394
7574
 
7395
7575
  // ../server/src/services/key-adoption.ts
7396
- import { readdir as readdir3, readFile as readFile4, writeFile as writeFile6, mkdir as mkdir8, copyFile, rename as rename4, stat as stat5 } from "fs/promises";
7397
- import { join as join10 } from "path";
7576
+ import { readdir as readdir3, readFile as readFile5, writeFile as writeFile6, mkdir as mkdir8, copyFile, rename as rename4, stat as stat5 } from "fs/promises";
7577
+ import { join as join11 } from "path";
7398
7578
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
7399
7579
  var ALGORITHM = "aes-256-gcm";
7400
7580
  function isAgentTriplet(value) {
@@ -7474,10 +7654,10 @@ async function adoptOrgKey(orgId, newKey, masterPassword, now) {
7474
7654
  const touchedRoots = /* @__PURE__ */ new Set();
7475
7655
  for (const vaultRoot of await rootsOfOrg(orgId)) {
7476
7656
  for (const name of await jsonFilesIn(vaultRoot)) {
7477
- const path = join10(vaultRoot, name);
7657
+ const path = join11(vaultRoot, name);
7478
7658
  let parsed;
7479
7659
  try {
7480
- parsed = JSON.parse(await readFile4(path, "utf-8"));
7660
+ parsed = JSON.parse(await readFile5(path, "utf-8"));
7481
7661
  } catch {
7482
7662
  continue;
7483
7663
  }
@@ -7491,14 +7671,14 @@ async function adoptOrgKey(orgId, newKey, masterPassword, now) {
7491
7671
  const stamp = now.toISOString().replace(/[:.]/g, "-").substring(0, 19);
7492
7672
  const backups = [];
7493
7673
  for (const vaultRoot of touchedRoots) {
7494
- const backupDir = join10(vaultRoot, `backup-key-${stamp}`);
7674
+ const backupDir = join11(vaultRoot, `backup-key-${stamp}`);
7495
7675
  await mkdir8(backupDir, { recursive: true });
7496
7676
  for (const name of await jsonFilesIn(vaultRoot)) {
7497
- await copyFile(join10(vaultRoot, name), join10(backupDir, name));
7677
+ await copyFile(join11(vaultRoot, name), join11(backupDir, name));
7498
7678
  }
7499
7679
  for (const name of [".encryption.key.enc", ".runeya-verify"]) {
7500
- if (await exists(join10(vaultRoot, name))) {
7501
- await copyFile(join10(vaultRoot, name), join10(backupDir, name));
7680
+ if (await exists(join11(vaultRoot, name))) {
7681
+ await copyFile(join11(vaultRoot, name), join11(backupDir, name));
7502
7682
  }
7503
7683
  }
7504
7684
  backups.push(backupDir);
@@ -7536,6 +7716,7 @@ async function accountScope(orgId) {
7536
7716
  const accountId = await accountForOrg(orgId);
7537
7717
  return accountId ? { accountId } : {};
7538
7718
  }
7719
+ var OPENING_TIMEOUT_MS = 1e4;
7539
7720
  async function assertOrgReadable(orgId) {
7540
7721
  try {
7541
7722
  await assertOrgVaultReadable(orgId);
@@ -7594,6 +7775,7 @@ var cloudSyncRouter = router({
7594
7775
  } else if (input.accountId) {
7595
7776
  await stampOrgCloud(input.orgId, input.cloudUrl, void 0, input.accountId);
7596
7777
  }
7778
+ await cloudSyncBridge.whenOpened(input.orgId, OPENING_TIMEOUT_MS);
7597
7779
  try {
7598
7780
  const pulled = await cloudSyncBridge.followProject(input.orgId, input.projectId);
7599
7781
  return { pulled, state: syncState(input.orgId) };
@@ -7963,13 +8145,13 @@ var cloudSyncRouter = router({
7963
8145
  // ../server/src/trpc/routers/filesystem.ts
7964
8146
  import { readdir as readdir4, stat as stat6 } from "fs/promises";
7965
8147
  import { homedir as homedir3 } from "os";
7966
- import { isAbsolute, join as join11, resolve as resolve2, dirname as dirname4, basename } from "path";
8148
+ import { isAbsolute, join as join12, resolve as resolve2, dirname as dirname5, basename } from "path";
7967
8149
  import { z as z24 } from "zod";
7968
8150
  var MAX_ENTRIES2 = 500;
7969
8151
  var PATH_SCHEMA = z24.string().min(1).max(4096);
7970
8152
  function safeResolve(input) {
7971
8153
  if (input.includes("\0")) throw new Error("Chemin invalide");
7972
- const expanded = input.startsWith("~") ? join11(homedir3(), input.slice(1)) : input;
8154
+ const expanded = input.startsWith("~") ? join12(homedir3(), input.slice(1)) : input;
7973
8155
  if (!isAbsolute(expanded)) throw new Error("Le chemin doit \xEAtre absolu");
7974
8156
  return resolve2(expanded);
7975
8157
  }
@@ -8005,13 +8187,13 @@ var filesystemRouter = router({
8005
8187
  const truncated = names.length > MAX_ENTRIES2;
8006
8188
  const entries2 = [];
8007
8189
  for (const name of names.slice(0, MAX_ENTRIES2)) {
8008
- const path = join11(dir, name);
8190
+ const path = join12(dir, name);
8009
8191
  entries2.push({ name, path, hasRuneyaProject: await holdsRuneyaProject(path) });
8010
8192
  }
8011
8193
  return {
8012
8194
  path: dir,
8013
8195
  // Null at the filesystem root, which is what stops "up" from looping.
8014
- parent: dirname4(dir) === dir ? null : dirname4(dir),
8196
+ parent: dirname5(dir) === dir ? null : dirname5(dir),
8015
8197
  name: basename(dir) || dir,
8016
8198
  entries: entries2,
8017
8199
  truncated,
@@ -8266,8 +8448,8 @@ async function ensureFilePermissions() {
8266
8448
  }
8267
8449
 
8268
8450
  // ../server/src/services/vault-migration.ts
8269
- import { readdir as readdir5, readFile as readFile5, writeFile as writeFile7, mkdir as mkdir9, copyFile as copyFile2, unlink, rename as rename5, stat as stat8 } from "fs/promises";
8270
- import { join as join12 } from "path";
8451
+ import { readdir as readdir5, readFile as readFile6, writeFile as writeFile7, mkdir as mkdir9, copyFile as copyFile2, unlink, rename as rename5, stat as stat8 } from "fs/promises";
8452
+ import { join as join13 } from "path";
8271
8453
  var KEY_FILES = [".encryption.key.enc", ".runeya-verify"];
8272
8454
  var MACHINE_FILES = [
8273
8455
  "agents.json",
@@ -8297,17 +8479,17 @@ async function openLegacyVault(legacyRoot, masterPassword) {
8297
8479
  }
8298
8480
  async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machineKey, now) {
8299
8481
  const stamp = now.toISOString().replace(/[:.]/g, "-").substring(0, 19);
8300
- const backupDir = join12(legacyRoot, `backup-${stamp}`);
8482
+ const backupDir = join13(legacyRoot, `backup-${stamp}`);
8301
8483
  await mkdir9(backupDir, { recursive: true });
8302
8484
  const jsonFiles = await vaultJsonFiles(legacyRoot);
8303
8485
  for (const name of [...jsonFiles, ...KEY_FILES]) {
8304
- if (await exists2(join12(legacyRoot, name))) {
8305
- await copyFile2(join12(legacyRoot, name), join12(backupDir, name));
8486
+ if (await exists2(join13(legacyRoot, name))) {
8487
+ await copyFile2(join13(legacyRoot, name), join13(backupDir, name));
8306
8488
  }
8307
8489
  }
8308
8490
  const rekeyed = [];
8309
8491
  for (const name of jsonFiles) {
8310
- const raw = await readFile5(join12(legacyRoot, name), "utf-8");
8492
+ const raw = await readFile6(join13(legacyRoot, name), "utf-8");
8311
8493
  let parsed;
8312
8494
  try {
8313
8495
  parsed = JSON.parse(raw);
@@ -8317,13 +8499,13 @@ async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machine
8317
8499
  const counter = { n: 0 };
8318
8500
  const converted = rekeyValue(parsed, legacyKey, machineKey, counter);
8319
8501
  if (counter.n === 0) continue;
8320
- const tmp = join12(legacyRoot, `${name}.tmp`);
8502
+ const tmp = join13(legacyRoot, `${name}.tmp`);
8321
8503
  await writeFile7(tmp, JSON.stringify(converted, null, 2), "utf-8");
8322
- await rename5(tmp, join12(legacyRoot, name));
8504
+ await rename5(tmp, join13(legacyRoot, name));
8323
8505
  rekeyed.push({ file: name, values: counter.n });
8324
8506
  }
8325
8507
  for (const { file, values } of rekeyed) {
8326
- const parsed = JSON.parse(await readFile5(join12(legacyRoot, file), "utf-8"));
8508
+ const parsed = JSON.parse(await readFile6(join13(legacyRoot, file), "utf-8"));
8327
8509
  const check = { n: 0 };
8328
8510
  rekeyValue(parsed, machineKey, machineKey, check);
8329
8511
  if (check.n !== values) {
@@ -8336,9 +8518,9 @@ async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machine
8336
8518
  const moved = [];
8337
8519
  const skipped = [];
8338
8520
  for (const name of MACHINE_FILES) {
8339
- const from = join12(legacyRoot, name);
8521
+ const from = join13(legacyRoot, name);
8340
8522
  if (!await exists2(from)) continue;
8341
- const to = join12(machineRootDir, name);
8523
+ const to = join13(machineRootDir, name);
8342
8524
  if (await exists2(to)) {
8343
8525
  skipped.push(name);
8344
8526
  continue;
@@ -8348,15 +8530,15 @@ async function migrateLegacyVault(legacyRoot, machineRootDir, legacyKey, machine
8348
8530
  moved.push(name);
8349
8531
  }
8350
8532
  for (const name of KEY_FILES) {
8351
- const path = join12(legacyRoot, name);
8533
+ const path = join13(legacyRoot, name);
8352
8534
  if (await exists2(path)) await unlink(path);
8353
8535
  }
8354
8536
  return { backupDir, rekeyed, moved, skipped };
8355
8537
  }
8356
8538
 
8357
8539
  // ../server/src/services/settings-migration.ts
8358
- import { readFile as readFile6, writeFile as writeFile8, rename as rename6, unlink as unlink2, mkdir as mkdir10, chmod as chmod4, stat as stat9 } from "fs/promises";
8359
- import { join as join13 } from "path";
8540
+ import { readFile as readFile7, writeFile as writeFile8, rename as rename6, unlink as unlink2, mkdir as mkdir10, chmod as chmod4, stat as stat9 } from "fs/promises";
8541
+ import { join as join14 } from "path";
8360
8542
  var SETTINGS_FILE = "settings.json";
8361
8543
  var SETTINGS_PRIVATE_FILE = "settings.private.json";
8362
8544
  async function exists3(path) {
@@ -8370,8 +8552,8 @@ async function legacySource(filename) {
8370
8552
  }
8371
8553
  async function moveInto(root, filename, from) {
8372
8554
  await mkdir10(root, { recursive: true });
8373
- const to = join13(root, filename);
8374
- const contents = await readFile6(from, "utf-8");
8555
+ const to = join14(root, filename);
8556
+ const contents = await readFile7(from, "utf-8");
8375
8557
  const tmp = to + ".tmp";
8376
8558
  await writeFile8(tmp, contents, "utf-8");
8377
8559
  await rename6(tmp, to);
@@ -8395,7 +8577,7 @@ async function migrateSettingsIntoVaults() {
8395
8577
  const orgId = orgs[0];
8396
8578
  const root = orgVaultRoot(orgId);
8397
8579
  for (const { filename, from } of sources) {
8398
- if (await exists3(join13(root, filename))) continue;
8580
+ if (await exists3(join14(root, filename))) continue;
8399
8581
  await moveInto(root, filename, from);
8400
8582
  report.files.push(filename);
8401
8583
  }
@@ -8409,16 +8591,16 @@ async function migrateSettingsIntoVaults() {
8409
8591
  // ../server/src/migrations/migrate-from-legacy.ts
8410
8592
  import { createRequire } from "module";
8411
8593
  import { randomBytes as randomBytes3, createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3 } from "crypto";
8412
- import { randomUUID as randomUUID6 } from "crypto";
8594
+ import { randomUUID as randomUUID7 } from "crypto";
8413
8595
  import {
8414
- readFile as readFile7,
8596
+ readFile as readFile8,
8415
8597
  writeFile as writeFile9,
8416
8598
  rename as rename7,
8417
8599
  mkdir as mkdir11,
8418
8600
  access as access2,
8419
8601
  readdir as readdir6
8420
8602
  } from "fs/promises";
8421
- import { join as join14, dirname as dirname5 } from "path";
8603
+ import { join as join15, dirname as dirname6 } from "path";
8422
8604
  var _require = createRequire(import.meta.url);
8423
8605
  var PARSER_ID_MAP = {
8424
8606
  "stack-monitor-parser-jsons": "native:json",
@@ -8535,7 +8717,7 @@ async function getSodium() {
8535
8717
  return _sodium;
8536
8718
  }
8537
8719
  async function decryptLegacyFile(filePath, key, sodium) {
8538
- const raw = (await readFile7(filePath, "utf-8")).trim();
8720
+ const raw = (await readFile8(filePath, "utf-8")).trim();
8539
8721
  if (!raw || raw === "[]" || raw === "{}") {
8540
8722
  try {
8541
8723
  return JSON.parse(raw);
@@ -8560,7 +8742,7 @@ async function atomicWrite(path, data) {
8560
8742
  }
8561
8743
  async function runLegacyMigration(dataDir) {
8562
8744
  const legacyRoot = env.DATA_DIR;
8563
- const legacyKeyPath = join14(legacyRoot, "dbs", "encryption-key.json");
8745
+ const legacyKeyPath = join15(legacyRoot, "dbs", "encryption-key.json");
8564
8746
  try {
8565
8747
  await access2(legacyKeyPath);
8566
8748
  } catch {
@@ -8571,17 +8753,17 @@ async function runLegacyMigration(dataDir) {
8571
8753
  const legacyTmp = `${legacyRoot}-tmp`;
8572
8754
  await rename7(legacyRoot, legacyTmp);
8573
8755
  await mkdir11(dataDir, { recursive: true });
8574
- const legacyOld = join14(dataDir, `backup-${timestamp}`);
8756
+ const legacyOld = join15(dataDir, `backup-${timestamp}`);
8575
8757
  await rename7(legacyTmp, legacyOld);
8576
8758
  console.log(`[legacy-migration] Legacy data backed up to ${legacyOld}`);
8577
- const keyJson = JSON.parse(await readFile7(join14(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
8759
+ const keyJson = JSON.parse(await readFile8(join15(legacyOld, "dbs", "encryption-key.json"), "utf-8"));
8578
8760
  const sodium = await getSodium();
8579
8761
  const sodiumKey = sodium.from_base64(keyJson.encryptionKey);
8580
8762
  const keyBuffer = Buffer.from(sodiumKey);
8581
8763
  const runyeaKeyHex = keyBuffer.toString("hex");
8582
8764
  await setEncryptionKey(runyeaKeyHex);
8583
8765
  console.log("[legacy-migration] Encryption key loaded in memory (temporary)");
8584
- const dbsDir = join14(legacyOld, "dbs");
8766
+ const dbsDir = join15(legacyOld, "dbs");
8585
8767
  async function readEncrypted(filePath) {
8586
8768
  try {
8587
8769
  return await decryptLegacyFile(filePath, sodiumKey, sodium);
@@ -8590,29 +8772,29 @@ async function runLegacyMigration(dataDir) {
8590
8772
  return null;
8591
8773
  }
8592
8774
  }
8593
- const envsDir = join14(dbsDir, "envs");
8775
+ const envsDir = join15(dbsDir, "envs");
8594
8776
  const envFiles = (await readdir6(envsDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
8595
8777
  const legacyEnvs = {};
8596
8778
  for (const file of envFiles) {
8597
8779
  const name = file.replace(".encrypted.json", "");
8598
- legacyEnvs[name] = await readEncrypted(join14(envsDir, file));
8780
+ legacyEnvs[name] = await readEncrypted(join15(envsDir, file));
8599
8781
  }
8600
- const servicesDir = join14(dbsDir, "services");
8782
+ const servicesDir = join15(dbsDir, "services");
8601
8783
  const serviceFiles = (await readdir6(servicesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
8602
8784
  const legacyServices = {};
8603
8785
  for (const file of serviceFiles) {
8604
8786
  const name = file.replace(".encrypted.json", "");
8605
8787
  if (!name) continue;
8606
- const data = await readEncrypted(join14(servicesDir, file));
8788
+ const data = await readEncrypted(join15(servicesDir, file));
8607
8789
  if (data?.label) legacyServices[name] = data;
8608
8790
  }
8609
- const overridesDir = join14(dbsDir, "overrides");
8791
+ const overridesDir = join15(dbsDir, "overrides");
8610
8792
  const overrideFiles = (await readdir6(overridesDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
8611
8793
  const legacyServiceOverrides = {};
8612
8794
  const legacyEnvOverrides = {};
8613
8795
  for (const file of overrideFiles) {
8614
8796
  const baseName = file.replace(".encrypted.json", "");
8615
- const data = await readEncrypted(join14(overridesDir, file));
8797
+ const data = await readEncrypted(join15(overridesDir, file));
8616
8798
  if (!data) continue;
8617
8799
  if (baseName.endsWith("-envs")) {
8618
8800
  legacyServiceOverrides[baseName.slice(0, -5)] = data;
@@ -8620,18 +8802,18 @@ async function runLegacyMigration(dataDir) {
8620
8802
  legacyEnvOverrides[baseName.slice(0, -12)] = data;
8621
8803
  }
8622
8804
  }
8623
- const parsersDir = join14(dbsDir, "parsers");
8805
+ const parsersDir = join15(dbsDir, "parsers");
8624
8806
  const parserFiles = (await readdir6(parsersDir).catch(() => [])).filter((f) => f.endsWith(".encrypted.json"));
8625
8807
  const legacyParsers = [];
8626
8808
  for (const file of parserFiles) {
8627
- const data = await readEncrypted(join14(parsersDir, file));
8809
+ const data = await readEncrypted(join15(parsersDir, file));
8628
8810
  if (data?.id) legacyParsers.push(data);
8629
8811
  }
8630
- const projectMonoId = randomUUID6();
8812
+ const projectMonoId = randomUUID7();
8631
8813
  const envIdMap = {};
8632
8814
  const labelToKey = {};
8633
8815
  for (const [name, legacyEnv] of Object.entries(legacyEnvs)) {
8634
- envIdMap[name] = randomUUID6();
8816
+ envIdMap[name] = randomUUID7();
8635
8817
  if (legacyEnv?.label) labelToKey[legacyEnv.label] = name;
8636
8818
  }
8637
8819
  const runeEnvs = [];
@@ -8656,7 +8838,7 @@ async function runLegacyMigration(dataDir) {
8656
8838
  if (hasInvalidInterpolation(value)) continue;
8657
8839
  const cleaned = cleanVarKey(key);
8658
8840
  if (inheritedVars[cleaned] === value) continue;
8659
- const varId = randomUUID6();
8841
+ const varId = randomUUID7();
8660
8842
  variables[varId] = makeVariable(cleaned, value);
8661
8843
  varIdLookup.set(`${envIdMap[name]}:${cleaned}`, varId);
8662
8844
  }
@@ -8677,7 +8859,7 @@ async function runLegacyMigration(dataDir) {
8677
8859
  const serviceIdMap = {};
8678
8860
  const runeServices = [];
8679
8861
  for (const [name, legacy] of Object.entries(legacyServices)) {
8680
- const id = randomUUID6();
8862
+ const id = randomUUID7();
8681
8863
  serviceIdMap[name] = id;
8682
8864
  const rootPath = legacy.rootPath || "";
8683
8865
  const rootPathCwd = rootPath && rootPath !== "." ? rootPath : "";
@@ -8686,7 +8868,7 @@ async function runLegacyMigration(dataDir) {
8686
8868
  const parts = [cmd.spawnCmd, ...cmd.spawnArgs || []].filter(Boolean);
8687
8869
  if (!parts.length) continue;
8688
8870
  commands.push({
8689
- id: cmd.id || randomUUID6(),
8871
+ id: cmd.id || randomUUID7(),
8690
8872
  label: "Launch",
8691
8873
  command: parts.join(" "),
8692
8874
  cwd: cmd.spawnOptions?.cwd || "",
@@ -8747,7 +8929,7 @@ async function runLegacyMigration(dataDir) {
8747
8929
  const legacyBootstrapCmds = container.bootstrap?.commands || [];
8748
8930
  if (legacyBootstrapCmds.length > 0) {
8749
8931
  dockerConfig.bootstrapCommands = legacyBootstrapCmds.filter((bs) => bs.cmd || bs.entrypoint).map((bs, idx) => ({
8750
- id: bs.id || randomUUID6(),
8932
+ id: bs.id || randomUUID7(),
8751
8933
  label: bs.label || `Bootstrap ${idx + 1}`,
8752
8934
  command: bs.cmd || "",
8753
8935
  ...bs.entrypoint ? { entrypoint: bs.entrypoint } : {},
@@ -8795,7 +8977,7 @@ async function runLegacyMigration(dataDir) {
8795
8977
  cwd: serviceCwd,
8796
8978
  runner: dockerConfig ? "docker" : "native",
8797
8979
  shortcuts: (legacy.shortcuts || []).filter((s) => s.spawnCmd || s.label).map((s) => ({
8798
- id: s.id || randomUUID6(),
8980
+ id: s.id || randomUUID7(),
8799
8981
  label: s.label || s.spawnCmd || "",
8800
8982
  command: [s.spawnCmd, ...s.spawnArgs || []].filter(Boolean).join(" "),
8801
8983
  ...s.spawnOptions?.cwd ? { cwd: s.spawnOptions.cwd } : {}
@@ -8835,7 +9017,7 @@ async function runLegacyMigration(dataDir) {
8835
9017
  );
8836
9018
  if (!svcScopedEnv) {
8837
9019
  svcScopedEnv = {
8838
- id: randomUUID6(),
9020
+ id: randomUUID7(),
8839
9021
  projectId: projectMonoId,
8840
9022
  name: `${service?.name || name} (${globalEnv.name})`,
8841
9023
  scope: "service",
@@ -8859,7 +9041,7 @@ async function runLegacyMigration(dataDir) {
8859
9041
  const varPost = typeof varData === "object" ? String(varData.suffix ?? "") : "";
8860
9042
  if (!varKey || existingKeys.has(cleanVarKey(varKey))) continue;
8861
9043
  const cleaned = cleanVarKey(varKey);
8862
- const varId = randomUUID6();
9044
+ const varId = randomUUID7();
8863
9045
  svcScopedEnv.variables[varId] = makeVariable(cleaned, varValue, varPre, varPost);
8864
9046
  existingKeys.add(cleaned);
8865
9047
  }
@@ -8879,7 +9061,7 @@ async function runLegacyMigration(dataDir) {
8879
9061
  );
8880
9062
  if (!svcScopedEnv) {
8881
9063
  svcScopedEnv = {
8882
- id: randomUUID6(),
9064
+ id: randomUUID7(),
8883
9065
  projectId: projectMonoId,
8884
9066
  name: `${service?.name || serviceName} (${globalEnv.name})`,
8885
9067
  scope: "service",
@@ -8900,7 +9082,7 @@ async function runLegacyMigration(dataDir) {
8900
9082
  const baseEntry = Object.entries(svcScopedEnv.variables).find(([, v]) => v.key === cleaned);
8901
9083
  let varId = baseEntry?.[0];
8902
9084
  if (!varId) {
8903
- varId = randomUUID6();
9085
+ varId = randomUUID7();
8904
9086
  svcScopedEnv.variables[varId] = {
8905
9087
  key: cleaned,
8906
9088
  value: { value: "", pre: "", post: "", isSecret: false, shareWithAi: false, injectIntoAi: false, required: false, overrideOnly: false, description: "" }
@@ -8930,13 +9112,13 @@ async function runLegacyMigration(dataDir) {
8930
9112
  flatOverrides[varId] = makeVariable(cleaned, value);
8931
9113
  }
8932
9114
  } else {
8933
- flatOverrides[randomUUID6()] = makeVariable(cleaned, value);
9115
+ flatOverrides[randomUUID7()] = makeVariable(cleaned, value);
8934
9116
  }
8935
9117
  } else {
8936
9118
  const cleaned = cleanVarKey(key);
8937
9119
  const existingVarId = Object.entries(env2.variables).find(([, v]) => v.key === cleaned)?.[0];
8938
9120
  if (!existingVarId) {
8939
- const varId = randomUUID6();
9121
+ const varId = randomUUID7();
8940
9122
  env2.variables[varId] = makeVariable(cleaned, value);
8941
9123
  varIdLookup.set(`${envId}:${cleaned}`, varId);
8942
9124
  }
@@ -8965,7 +9147,7 @@ async function runLegacyMigration(dataDir) {
8965
9147
  }
8966
9148
  let existingSettings = {};
8967
9149
  try {
8968
- existingSettings = JSON.parse(await readFile7(join14(dataDir, "settings.json"), "utf-8"));
9150
+ existingSettings = JSON.parse(await readFile8(join15(dataDir, "settings.json"), "utf-8"));
8969
9151
  } catch {
8970
9152
  }
8971
9153
  const migratedParsers = legacyParsers.map((p) => ({
@@ -8986,11 +9168,11 @@ ${p.transform ?? ""}`,
8986
9168
  ...migratedParsers
8987
9169
  ]
8988
9170
  };
8989
- await atomicWrite(join14(dataDir, "projects.json"), runeProjects);
8990
- await atomicWrite(join14(dataDir, "services.json"), runeServices);
8991
- await atomicWrite(join14(dataDir, "environments.json"), runeEnvs);
8992
- await atomicWrite(join14(dataDir, "environment-overrides.json"), flatOverrides);
8993
- await atomicWrite(join14(dataDir, "settings.json"), newSettings);
9171
+ await atomicWrite(join15(dataDir, "projects.json"), runeProjects);
9172
+ await atomicWrite(join15(dataDir, "services.json"), runeServices);
9173
+ await atomicWrite(join15(dataDir, "environments.json"), runeEnvs);
9174
+ await atomicWrite(join15(dataDir, "environment-overrides.json"), flatOverrides);
9175
+ await atomicWrite(join15(dataDir, "settings.json"), newSettings);
8994
9176
  const svcEnvCount = runeEnvs.filter((e) => e.scope === "service").length;
8995
9177
  console.log(`[legacy-migration] Written: ${runeProjects.length} projects, ${runeServices.length} services, ${runeEnvs.length} environments (${svcEnvCount} service-scoped)`);
8996
9178
  console.log(`[legacy-migration] Overrides: ${Object.keys(flatOverrides).length}, custom parsers: ${migratedParsers.length} (all disabled \u2014 rewrite required)`);
@@ -9004,9 +9186,9 @@ async function findLegacyKeyInBackup(dataDir) {
9004
9186
  const entries2 = await readdir6(dataDir);
9005
9187
  const backupDirs = entries2.filter((e) => e.startsWith("backup-")).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).reverse();
9006
9188
  for (const dir of backupDirs) {
9007
- const keyJsonPath = join14(dataDir, dir, "dbs", "encryption-key.json");
9189
+ const keyJsonPath = join15(dataDir, dir, "dbs", "encryption-key.json");
9008
9190
  try {
9009
- const raw = await readFile7(keyJsonPath, "utf-8");
9191
+ const raw = await readFile8(keyJsonPath, "utf-8");
9010
9192
  const parsed = JSON.parse(raw);
9011
9193
  if (!parsed.encryptionKey) continue;
9012
9194
  const sodium = await getSodium();
@@ -9027,8 +9209,8 @@ async function runOverrideOnlyMigration(dataDir) {
9027
9209
  const { resolve: resolve4 } = await import("path");
9028
9210
  const abDataDir = resolve4(dataDir);
9029
9211
  const candidateDirs = [
9030
- join14(dirname5(env.DATA_DIR), ".runeya", "dbs", "overrides"),
9031
- join14(abDataDir, "dbs", "overrides")
9212
+ join15(dirname6(env.DATA_DIR), ".runeya", "dbs", "overrides"),
9213
+ join15(abDataDir, "dbs", "overrides")
9032
9214
  ];
9033
9215
  let overridesDir = null;
9034
9216
  let overrideFiles = [];
@@ -9044,7 +9226,7 @@ async function runOverrideOnlyMigration(dataDir) {
9044
9226
  }
9045
9227
  }
9046
9228
  if (!overridesDir || overrideFiles.length === 0) return false;
9047
- const legacyKeyPath = join14(overridesDir, "..", "encryption-key.json");
9229
+ const legacyKeyPath = join15(overridesDir, "..", "encryption-key.json");
9048
9230
  try {
9049
9231
  await access2(legacyKeyPath);
9050
9232
  return false;
@@ -9062,7 +9244,7 @@ async function runOverrideOnlyMigration(dataDir) {
9062
9244
  const envNameToId = {};
9063
9245
  let envsRaw;
9064
9246
  try {
9065
- envsRaw = JSON.parse(await readFile7(join14(abDataDir, "environments.json"), "utf-8"));
9247
+ envsRaw = JSON.parse(await readFile8(join15(abDataDir, "environments.json"), "utf-8"));
9066
9248
  for (const _env of envsRaw) {
9067
9249
  const env2 = _env;
9068
9250
  if (env2["name"] && env2["id"]) {
@@ -9081,7 +9263,7 @@ async function runOverrideOnlyMigration(dataDir) {
9081
9263
  let servicesRaw = [];
9082
9264
  const serviceNameToId = {};
9083
9265
  try {
9084
- servicesRaw = JSON.parse(await readFile7(join14(abDataDir, "services.json"), "utf-8"));
9266
+ servicesRaw = JSON.parse(await readFile8(join15(abDataDir, "services.json"), "utf-8"));
9085
9267
  for (const _svc of servicesRaw) {
9086
9268
  const svc = _svc;
9087
9269
  if (svc["name"] && svc["id"]) serviceNameToId[svc["name"]] = svc["id"];
@@ -9098,7 +9280,7 @@ async function runOverrideOnlyMigration(dataDir) {
9098
9280
  const envName = baseName.slice(0, -"-environment".length);
9099
9281
  let data;
9100
9282
  try {
9101
- data = await decryptLegacyFile(join14(overridesDir, file), sodiumKey, sodium);
9283
+ data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
9102
9284
  } catch (err) {
9103
9285
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9104
9286
  continue;
@@ -9120,12 +9302,12 @@ async function runOverrideOnlyMigration(dataDir) {
9120
9302
  if (isSameAsBase(value, baseVar)) continue;
9121
9303
  flatOverrides[varId] = makeVariable(cleaned, value);
9122
9304
  } else {
9123
- flatOverrides[randomUUID6()] = makeVariable(cleaned, value);
9305
+ flatOverrides[randomUUID7()] = makeVariable(cleaned, value);
9124
9306
  }
9125
9307
  }
9126
9308
  }
9127
9309
  if (Object.keys(flatOverrides).length > 0) {
9128
- await atomicWrite(join14(abDataDir, "environment-overrides.json"), flatOverrides);
9310
+ await atomicWrite(join15(abDataDir, "environment-overrides.json"), flatOverrides);
9129
9311
  console.log(`[override-migration] Written ${Object.keys(flatOverrides).length} override(s) to environment-overrides.json`);
9130
9312
  }
9131
9313
  let envsModified = false;
@@ -9135,7 +9317,7 @@ async function runOverrideOnlyMigration(dataDir) {
9135
9317
  const serviceName = baseName.slice(0, -"-envs".length);
9136
9318
  let data;
9137
9319
  try {
9138
- data = await decryptLegacyFile(join14(overridesDir, file), sodiumKey, sodium);
9320
+ data = await decryptLegacyFile(join15(overridesDir, file), sodiumKey, sodium);
9139
9321
  } catch (err) {
9140
9322
  console.warn(`[override-migration] Failed to decrypt ${file}: ${err.message}`);
9141
9323
  continue;
@@ -9157,7 +9339,7 @@ async function runOverrideOnlyMigration(dataDir) {
9157
9339
  );
9158
9340
  if (!svcScopedEnv) {
9159
9341
  svcScopedEnv = {
9160
- id: randomUUID6(),
9342
+ id: randomUUID7(),
9161
9343
  projectId: globalEnv["projectId"],
9162
9344
  name: `${service?.["name"] || serviceName} (${globalEnv["name"]})`,
9163
9345
  scope: "service",
@@ -9179,7 +9361,7 @@ async function runOverrideOnlyMigration(dataDir) {
9179
9361
  const baseEntry = Object.entries(svcScopedEnv["variables"]).find(([, v]) => v.key === cleaned);
9180
9362
  let varId = baseEntry?.[0];
9181
9363
  if (!varId) {
9182
- varId = randomUUID6();
9364
+ varId = randomUUID7();
9183
9365
  svcScopedEnv["variables"][varId] = {
9184
9366
  key: cleaned,
9185
9367
  value: { value: "", pre: "", post: "", isSecret: false, shareWithAi: false, injectIntoAi: false, required: false, overrideOnly: false, description: "" }
@@ -9194,7 +9376,7 @@ async function runOverrideOnlyMigration(dataDir) {
9194
9376
  }
9195
9377
  }
9196
9378
  if (envsModified) {
9197
- await atomicWrite(join14(abDataDir, "environments.json"), envsRaw);
9379
+ await atomicWrite(join15(abDataDir, "environments.json"), envsRaw);
9198
9380
  console.log("[override-migration] Merged service overrides into environments.json");
9199
9381
  }
9200
9382
  const migratedDir = overridesDir.replace(/overrides$/, "overrides-migrated");
@@ -9209,7 +9391,7 @@ async function runOverrideOnlyMigration(dataDir) {
9209
9391
  }
9210
9392
 
9211
9393
  // ../server/src/services/server-state.ts
9212
- import { join as join15 } from "path";
9394
+ import { join as join16 } from "path";
9213
9395
  import { stat as stat10 } from "fs/promises";
9214
9396
  var ENCRYPTED_KEY_FILENAME2 = ".encryption.key.enc";
9215
9397
  var VERIFY_FILENAME2 = ".runeya-verify";
@@ -9219,22 +9401,22 @@ async function getServerState() {
9219
9401
  let hasVerify = false;
9220
9402
  let hasData = false;
9221
9403
  try {
9222
- await stat10(join15(dataDir, ENCRYPTED_KEY_FILENAME2));
9404
+ await stat10(join16(dataDir, ENCRYPTED_KEY_FILENAME2));
9223
9405
  hasEncFile = true;
9224
9406
  } catch {
9225
9407
  }
9226
9408
  try {
9227
- await stat10(join15(dataDir, VERIFY_FILENAME2));
9409
+ await stat10(join16(dataDir, VERIFY_FILENAME2));
9228
9410
  hasVerify = true;
9229
9411
  } catch {
9230
9412
  }
9231
9413
  try {
9232
- await stat10(join15(dataDir, "projects.json"));
9414
+ await stat10(join16(dataDir, "projects.json"));
9233
9415
  hasData = true;
9234
9416
  } catch {
9235
9417
  }
9236
9418
  try {
9237
- await stat10(join15(dataDir, "project.json"));
9419
+ await stat10(join16(dataDir, "project.json"));
9238
9420
  hasData = true;
9239
9421
  } catch {
9240
9422
  }
@@ -9754,6 +9936,11 @@ async function createLocalServer() {
9754
9936
  await serviceStore.list().catch((err) => {
9755
9937
  console.warn("[server] Failed to preload services:", err);
9756
9938
  });
9939
+ void (async () => {
9940
+ const orgRoots = (await listOrgVaults()).map((orgId) => orgVaultRoot(orgId));
9941
+ const removed = await sweepStaleTempFiles([machineRoot(), ...orgRoots, ...await allVaultRoots()]);
9942
+ if (removed > 0) console.log(`[server] ${removed} fichier(s) temporaire(s) orphelin(s) retir\xE9(s)`);
9943
+ })().catch((err) => console.warn("[server] Temp file sweep failed:", err));
9757
9944
  cloudSyncBridge.start().catch((err) => {
9758
9945
  console.warn("[server] Failed to start cloud sync bridge:", err);
9759
9946
  });
@@ -10369,7 +10556,7 @@ async function createLocalServer() {
10369
10556
  stopClaudeTranscriptWatcher();
10370
10557
  stopCodexTranscriptWatcher();
10371
10558
  imageCleanupService.stop();
10372
- cloudSyncBridge.stopAll();
10559
+ void cloudSyncBridge.stopAll();
10373
10560
  await Promise.all([
10374
10561
  unregisterInstance(),
10375
10562
  agentSpawner.shutdown(),
@@ -10386,7 +10573,7 @@ async function createLocalServer() {
10386
10573
  ]);
10387
10574
  console.log("[server] Shutdown complete");
10388
10575
  };
10389
- const { initCompanion } = await import("./companion-AUMA6RGX.js");
10576
+ const { initCompanion } = await import("./companion-7VOV4W3T.js");
10390
10577
  initCompanion();
10391
10578
  return { app, server, wss, shutdown };
10392
10579
  }
@@ -10517,7 +10704,7 @@ function onTrayServicesChanged(listener) {
10517
10704
 
10518
10705
  // ../server/src/services/tray-icon.ts
10519
10706
  import { mkdir as mkdir12, writeFile as writeFile10, rename as rename8 } from "fs/promises";
10520
- import { join as join16 } from "path";
10707
+ import { join as join17 } from "path";
10521
10708
 
10522
10709
  // ../server/src/services/png-to-ico.ts
10523
10710
  function isPng(bytes) {
@@ -10677,7 +10864,7 @@ var FETCH_TIMEOUT_MS = 3e3;
10677
10864
  var CACHE_TTL_MS = 10 * 6e4;
10678
10865
  var cache2 = /* @__PURE__ */ new Map();
10679
10866
  function trayIconDir() {
10680
- return join16(machineRoot(), "tray-icons");
10867
+ return join17(machineRoot(), "tray-icons");
10681
10868
  }
10682
10869
  async function resolveTrayIcon(platform = process.platform) {
10683
10870
  const orgId = getCurrentProject()?.orgId;
@@ -10706,7 +10893,7 @@ async function renderThemedIcon(platform) {
10706
10893
  async function writeTrayIcon(stem, png, platform) {
10707
10894
  const dir = trayIconDir();
10708
10895
  await mkdir12(dir, { recursive: true });
10709
- const path = join16(dir, stem);
10896
+ const path = join17(dir, stem);
10710
10897
  if (platform === "win32") {
10711
10898
  const ico = pngToIco(png);
10712
10899
  return ico ? writeAtomic(`${path}.ico`, ico) : null;
@@ -10833,4 +11020,4 @@ export {
10833
11020
  startProcess,
10834
11021
  stopProcess
10835
11022
  };
10836
- //# sourceMappingURL=src-JIS3X4OU.js.map
11023
+ //# sourceMappingURL=src-DN2IGASI.js.map