@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.
- package/{agent-manager-4HBMLHXU.js → agent-manager-2G55ZFCL.js} +3 -3
- package/{chat-VL6BIVO5.js → chat-MOAVENBI.js} +6 -6
- package/{chunk-HJUFIZ7H.js → chunk-DUHCY3JU.js} +128 -158
- package/chunk-DUHCY3JU.js.map +1 -0
- package/chunk-HK5QNU6P.js +117 -0
- package/chunk-HK5QNU6P.js.map +1 -0
- package/{chunk-CNTL6NVU.js → chunk-MSLFR2EN.js} +118 -58
- package/chunk-MSLFR2EN.js.map +1 -0
- package/{chunk-3N55WR7F.js → chunk-N3ZI35SC.js} +7 -16
- package/chunk-N3ZI35SC.js.map +1 -0
- package/{chunk-OVV52N5F.js → chunk-TNJFDWJX.js} +78 -74
- package/chunk-TNJFDWJX.js.map +1 -0
- package/{companion-AUMA6RGX.js → companion-7VOV4W3T.js} +4 -4
- package/index.js +3 -3
- package/package.json +1 -1
- package/{settings-manager-ROWQ5GC2.js → settings-manager-MAVD4GAS.js} +3 -3
- package/{src-JIS3X4OU.js → src-DN2IGASI.js} +297 -110
- package/src-DN2IGASI.js.map +1 -0
- package/chunk-3N55WR7F.js.map +0 -1
- package/chunk-CNTL6NVU.js.map +0 -1
- package/chunk-HJUFIZ7H.js.map +0 -1
- package/chunk-OVV52N5F.js.map +0 -1
- package/chunk-YZU5FNXR.js +0 -43
- package/chunk-YZU5FNXR.js.map +0 -1
- package/src-JIS3X4OU.js.map +0 -1
- /package/{agent-manager-4HBMLHXU.js.map → agent-manager-2G55ZFCL.js.map} +0 -0
- /package/{chat-VL6BIVO5.js.map → chat-MOAVENBI.js.map} +0 -0
- /package/{companion-AUMA6RGX.js.map → companion-7VOV4W3T.js.map} +0 -0
- /package/{settings-manager-ROWQ5GC2.js.map → settings-manager-MAVD4GAS.js.map} +0 -0
|
@@ -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-
|
|
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-
|
|
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-
|
|
126
|
+
} from "./chunk-MSLFR2EN.js";
|
|
129
127
|
import {
|
|
130
128
|
settingsManager
|
|
131
|
-
} from "./chunk-
|
|
132
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
|
7397
|
-
import { join as
|
|
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 =
|
|
7657
|
+
const path = join11(vaultRoot, name);
|
|
7478
7658
|
let parsed;
|
|
7479
7659
|
try {
|
|
7480
|
-
parsed = JSON.parse(await
|
|
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 =
|
|
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(
|
|
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(
|
|
7501
|
-
await copyFile(
|
|
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
|
|
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("~") ?
|
|
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 =
|
|
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:
|
|
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
|
|
8270
|
-
import { join as
|
|
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 =
|
|
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(
|
|
8305
|
-
await copyFile2(
|
|
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
|
|
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 =
|
|
8502
|
+
const tmp = join13(legacyRoot, `${name}.tmp`);
|
|
8321
8503
|
await writeFile7(tmp, JSON.stringify(converted, null, 2), "utf-8");
|
|
8322
|
-
await rename5(tmp,
|
|
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
|
|
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 =
|
|
8521
|
+
const from = join13(legacyRoot, name);
|
|
8340
8522
|
if (!await exists2(from)) continue;
|
|
8341
|
-
const to =
|
|
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 =
|
|
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
|
|
8359
|
-
import { join as
|
|
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 =
|
|
8374
|
-
const contents = await
|
|
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(
|
|
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
|
|
8594
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
8413
8595
|
import {
|
|
8414
|
-
readFile as
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
8780
|
+
legacyEnvs[name] = await readEncrypted(join15(envsDir, file));
|
|
8599
8781
|
}
|
|
8600
|
-
const servicesDir =
|
|
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(
|
|
8788
|
+
const data = await readEncrypted(join15(servicesDir, file));
|
|
8607
8789
|
if (data?.label) legacyServices[name] = data;
|
|
8608
8790
|
}
|
|
8609
|
-
const overridesDir =
|
|
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(
|
|
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 =
|
|
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(
|
|
8809
|
+
const data = await readEncrypted(join15(parsersDir, file));
|
|
8628
8810
|
if (data?.id) legacyParsers.push(data);
|
|
8629
8811
|
}
|
|
8630
|
-
const projectMonoId =
|
|
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] =
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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[
|
|
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 =
|
|
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
|
|
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(
|
|
8990
|
-
await atomicWrite(
|
|
8991
|
-
await atomicWrite(
|
|
8992
|
-
await atomicWrite(
|
|
8993
|
-
await atomicWrite(
|
|
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 =
|
|
9189
|
+
const keyJsonPath = join15(dataDir, dir, "dbs", "encryption-key.json");
|
|
9008
9190
|
try {
|
|
9009
|
-
const raw = await
|
|
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
|
-
|
|
9031
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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[
|
|
9305
|
+
flatOverrides[randomUUID7()] = makeVariable(cleaned, value);
|
|
9124
9306
|
}
|
|
9125
9307
|
}
|
|
9126
9308
|
}
|
|
9127
9309
|
if (Object.keys(flatOverrides).length > 0) {
|
|
9128
|
-
await atomicWrite(
|
|
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(
|
|
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:
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
9404
|
+
await stat10(join16(dataDir, ENCRYPTED_KEY_FILENAME2));
|
|
9223
9405
|
hasEncFile = true;
|
|
9224
9406
|
} catch {
|
|
9225
9407
|
}
|
|
9226
9408
|
try {
|
|
9227
|
-
await stat10(
|
|
9409
|
+
await stat10(join16(dataDir, VERIFY_FILENAME2));
|
|
9228
9410
|
hasVerify = true;
|
|
9229
9411
|
} catch {
|
|
9230
9412
|
}
|
|
9231
9413
|
try {
|
|
9232
|
-
await stat10(
|
|
9414
|
+
await stat10(join16(dataDir, "projects.json"));
|
|
9233
9415
|
hasData = true;
|
|
9234
9416
|
} catch {
|
|
9235
9417
|
}
|
|
9236
9418
|
try {
|
|
9237
|
-
await stat10(
|
|
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-
|
|
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
|
|
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
|
|
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 =
|
|
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-
|
|
11023
|
+
//# sourceMappingURL=src-DN2IGASI.js.map
|