lody 0.46.0 → 0.46.2-next.1
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/dist/index.js +655 -31
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -22780,7 +22780,7 @@ Event: ${getEventDescription(event)}`);
|
|
|
22780
22780
|
const mergedOptions = {
|
|
22781
22781
|
...options,
|
|
22782
22782
|
dsn: options.dsn ?? "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152",
|
|
22783
|
-
environment: options.environment ?? "
|
|
22783
|
+
environment: options.environment ?? "staging",
|
|
22784
22784
|
sendClientReports: options.sendClientReports ?? true,
|
|
22785
22785
|
transport: options.transport ?? makeNodeTransport,
|
|
22786
22786
|
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
|
|
@@ -36820,7 +36820,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
36820
36820
|
return client;
|
|
36821
36821
|
}
|
|
36822
36822
|
const name = "lody";
|
|
36823
|
-
const version$4 = "0.46.
|
|
36823
|
+
const version$4 = "0.46.2-next.1";
|
|
36824
36824
|
const description = "Lody Agent CLI tool for managing remote command execution";
|
|
36825
36825
|
const type = "module";
|
|
36826
36826
|
const main$3 = "dist/index.js";
|
|
@@ -37068,15 +37068,15 @@ Mongoose Error Code: ${error2.code}` : ""}`
|
|
|
37068
37068
|
return "dev";
|
|
37069
37069
|
}
|
|
37070
37070
|
};
|
|
37071
|
-
const getRuntimeEnv = () => normalizeRuntimeEnv("
|
|
37071
|
+
const getRuntimeEnv = () => normalizeRuntimeEnv("staging");
|
|
37072
37072
|
const isDevEnv = () => getRuntimeEnv() === "dev";
|
|
37073
37073
|
const runtimeEnv = getRuntimeEnv();
|
|
37074
|
-
const environment$1 = "
|
|
37074
|
+
const environment$1 = "staging";
|
|
37075
37075
|
const dsn = "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152";
|
|
37076
37076
|
const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://us.i.posthog.com";
|
|
37077
37077
|
const postHogKey = process.env.LODY_POSTHOG_KEY ?? "phc_LFS5i5WIwg4irAhrG5oJR04iYPhReVZ3DdFZOKqCkjG";
|
|
37078
|
-
const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) ||
|
|
37079
|
-
const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.
|
|
37078
|
+
const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) || 1;
|
|
37079
|
+
const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.25;
|
|
37080
37080
|
const sentryEnabled = runtimeEnv !== "dev" && true;
|
|
37081
37081
|
const postHogEnabled = runtimeEnv !== "dev" && process.env.LODY_POSTHOG_DISABLED !== "1";
|
|
37082
37082
|
const release = `${name}@${version$4}`;
|
|
@@ -64608,16 +64608,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
64608
64608
|
}
|
|
64609
64609
|
return _v4(options, buf, offset2);
|
|
64610
64610
|
}
|
|
64611
|
-
let LODY_AUTH_URL = "https://
|
|
64611
|
+
let LODY_AUTH_URL = "https://impressive-guineapig-165.convex.cloud";
|
|
64612
64612
|
let LODY_AUTH_SITE_URL = "";
|
|
64613
|
-
let LODY_SERVER_URL = "https://
|
|
64614
|
-
let SITE_URL = "https://lody.
|
|
64613
|
+
let LODY_SERVER_URL = "https://lody-server.lz-9c5.workers.dev";
|
|
64614
|
+
let SITE_URL = "https://main.lody.pages.dev";
|
|
64615
64615
|
let SITE_APP_BASE_PATH = "";
|
|
64616
64616
|
const loadEnv = () => {
|
|
64617
|
-
LODY_AUTH_URL = "https://
|
|
64617
|
+
LODY_AUTH_URL = "https://impressive-guineapig-165.convex.cloud";
|
|
64618
64618
|
LODY_AUTH_SITE_URL = "";
|
|
64619
|
-
LODY_SERVER_URL = "https://
|
|
64620
|
-
SITE_URL = "https://lody.
|
|
64619
|
+
LODY_SERVER_URL = "https://lody-server.lz-9c5.workers.dev";
|
|
64620
|
+
SITE_URL = "https://main.lody.pages.dev";
|
|
64621
64621
|
SITE_APP_BASE_PATH = process.env["SITE_APP_BASE_PATH"] ?? "";
|
|
64622
64622
|
};
|
|
64623
64623
|
const MACHINE_ID_FILE_NAME = "machine-id";
|
|
@@ -74740,6 +74740,59 @@ Task description:
|
|
|
74740
74740
|
}
|
|
74741
74741
|
return CONTAINER_TYPES.includes(value.kind());
|
|
74742
74742
|
}
|
|
74743
|
+
class EphemeralStore {
|
|
74744
|
+
constructor(timeout2 = 3e4) {
|
|
74745
|
+
this.inner = new EphemeralStoreWasm(timeout2);
|
|
74746
|
+
this.timeout = timeout2;
|
|
74747
|
+
}
|
|
74748
|
+
apply(bytes) {
|
|
74749
|
+
this.inner.apply(bytes);
|
|
74750
|
+
this.startTimerIfNotEmpty();
|
|
74751
|
+
}
|
|
74752
|
+
set(key2, value) {
|
|
74753
|
+
this.inner.set(key2, value);
|
|
74754
|
+
this.startTimerIfNotEmpty();
|
|
74755
|
+
}
|
|
74756
|
+
delete(key2) {
|
|
74757
|
+
this.inner.delete(key2);
|
|
74758
|
+
}
|
|
74759
|
+
get(key2) {
|
|
74760
|
+
return this.inner.get(key2);
|
|
74761
|
+
}
|
|
74762
|
+
getAllStates() {
|
|
74763
|
+
return this.inner.getAllStates();
|
|
74764
|
+
}
|
|
74765
|
+
encode(key2) {
|
|
74766
|
+
return this.inner.encode(key2);
|
|
74767
|
+
}
|
|
74768
|
+
encodeAll() {
|
|
74769
|
+
return this.inner.encodeAll();
|
|
74770
|
+
}
|
|
74771
|
+
keys() {
|
|
74772
|
+
return this.inner.keys();
|
|
74773
|
+
}
|
|
74774
|
+
destroy() {
|
|
74775
|
+
clearInterval(this.timer);
|
|
74776
|
+
}
|
|
74777
|
+
subscribe(listener) {
|
|
74778
|
+
return this.inner.subscribe(listener);
|
|
74779
|
+
}
|
|
74780
|
+
subscribeLocalUpdates(listener) {
|
|
74781
|
+
return this.inner.subscribeLocalUpdates(listener);
|
|
74782
|
+
}
|
|
74783
|
+
startTimerIfNotEmpty() {
|
|
74784
|
+
if (this.inner.isEmpty() || this.timer != null) {
|
|
74785
|
+
return;
|
|
74786
|
+
}
|
|
74787
|
+
this.timer = setInterval(() => {
|
|
74788
|
+
this.inner.removeOutdated();
|
|
74789
|
+
if (this.inner.isEmpty()) {
|
|
74790
|
+
clearInterval(this.timer);
|
|
74791
|
+
this.timer = void 0;
|
|
74792
|
+
}
|
|
74793
|
+
}, this.timeout / 2);
|
|
74794
|
+
}
|
|
74795
|
+
}
|
|
74743
74796
|
LoroDoc.prototype.toJsonWithReplacer = function(replacer) {
|
|
74744
74797
|
const processed = /* @__PURE__ */ new Set();
|
|
74745
74798
|
const doc = this;
|
|
@@ -82790,6 +82843,58 @@ ${tailedOutput}` : null;
|
|
|
82790
82843
|
compress: compressStreamsSnapshot,
|
|
82791
82844
|
decompress: decompressStreamsSnapshot
|
|
82792
82845
|
};
|
|
82846
|
+
const LODY_PRESENCE_CHANNEL = "presence";
|
|
82847
|
+
const LODY_PRESENCE_TTL_MS = 6e4;
|
|
82848
|
+
const LODY_PRESENCE_HEARTBEAT_MS = 2e4;
|
|
82849
|
+
const LODY_PRESENCE_STREAM_HEARTBEAT_MS = 15e3;
|
|
82850
|
+
const ActiveSessionStatusSchema = discriminatedUnion("type", [
|
|
82851
|
+
object({
|
|
82852
|
+
type: literal("running")
|
|
82853
|
+
}),
|
|
82854
|
+
object({
|
|
82855
|
+
type: literal("requestPermission"),
|
|
82856
|
+
requestId: string$2().optional(),
|
|
82857
|
+
toolCallId: string$2().optional(),
|
|
82858
|
+
toolTitle: string$2().optional()
|
|
82859
|
+
}),
|
|
82860
|
+
object({
|
|
82861
|
+
type: literal("initializing"),
|
|
82862
|
+
stage: _enum([
|
|
82863
|
+
"git-clone",
|
|
82864
|
+
"acp",
|
|
82865
|
+
"resuming"
|
|
82866
|
+
]).optional(),
|
|
82867
|
+
detail: string$2().optional()
|
|
82868
|
+
})
|
|
82869
|
+
]);
|
|
82870
|
+
const PresenceInstanceIdSchema = string$2().min(1).transform((value) => {
|
|
82871
|
+
return value;
|
|
82872
|
+
});
|
|
82873
|
+
const PresenceMachineStateSchema = object({
|
|
82874
|
+
kind: literal("machine"),
|
|
82875
|
+
machineId: string$2().min(1).transform((value) => value),
|
|
82876
|
+
instanceId: PresenceInstanceIdSchema,
|
|
82877
|
+
updatedAt: number$3().finite()
|
|
82878
|
+
});
|
|
82879
|
+
const PresenceSessionStateSchema = object({
|
|
82880
|
+
kind: literal("session"),
|
|
82881
|
+
sessionId: string$2().min(1).transform((value) => value),
|
|
82882
|
+
machineId: string$2().min(1).transform((value) => value),
|
|
82883
|
+
instanceId: PresenceInstanceIdSchema,
|
|
82884
|
+
status: ActiveSessionStatusSchema,
|
|
82885
|
+
updatedAt: number$3().finite()
|
|
82886
|
+
});
|
|
82887
|
+
discriminatedUnion("kind", [
|
|
82888
|
+
PresenceMachineStateSchema,
|
|
82889
|
+
PresenceSessionStateSchema
|
|
82890
|
+
]);
|
|
82891
|
+
const getLodyMachinePresenceKey = (machineId, instanceId) => `machine:${encodeURIComponent(machineId)}:${encodeURIComponent(instanceId)}`;
|
|
82892
|
+
const getLodySessionPresenceKey = (sessionId, instanceId) => `session:${encodeURIComponent(sessionId)}:${encodeURIComponent(instanceId)}`;
|
|
82893
|
+
const toLodyPresenceStreamUrl = (durableStreamUrl) => {
|
|
82894
|
+
const url = new URL(durableStreamUrl);
|
|
82895
|
+
url.searchParams.set("ephemeral", LODY_PRESENCE_CHANNEL);
|
|
82896
|
+
return url.toString();
|
|
82897
|
+
};
|
|
82793
82898
|
const isLoroRepoDocDeleted = (value) => {
|
|
82794
82899
|
if (!value || typeof value !== "object") {
|
|
82795
82900
|
return false;
|
|
@@ -92966,6 +93071,368 @@ stream:${scope2.streamId}`;
|
|
|
92966
93071
|
};
|
|
92967
93072
|
}
|
|
92968
93073
|
};
|
|
93074
|
+
function createNoopAdapter() {
|
|
93075
|
+
return {
|
|
93076
|
+
emptyVersion: () => ({}),
|
|
93077
|
+
mergeVersions: (base) => base,
|
|
93078
|
+
exportSnapshot: () => new Uint8Array(),
|
|
93079
|
+
applySnapshot: () => ({}),
|
|
93080
|
+
exportUpdates: () => void 0,
|
|
93081
|
+
applyRemoteUpdates: () => ({}),
|
|
93082
|
+
subscribeLocalUpdates: () => () => void 0
|
|
93083
|
+
};
|
|
93084
|
+
}
|
|
93085
|
+
function normalizeEphemeralUpdate(update2) {
|
|
93086
|
+
if (update2 == null || update2.byteLength === 0) return null;
|
|
93087
|
+
return update2;
|
|
93088
|
+
}
|
|
93089
|
+
function toError$1(error2) {
|
|
93090
|
+
return error2 instanceof Error ? error2 : new Error(String(error2));
|
|
93091
|
+
}
|
|
93092
|
+
function closedSubscriptionError() {
|
|
93093
|
+
return new Error("subscription closed");
|
|
93094
|
+
}
|
|
93095
|
+
var EphemeralStreamCrdt = class {
|
|
93096
|
+
streamUrl;
|
|
93097
|
+
adaptor;
|
|
93098
|
+
client;
|
|
93099
|
+
reconnectConfig;
|
|
93100
|
+
heartbeatMs;
|
|
93101
|
+
debug;
|
|
93102
|
+
joinState;
|
|
93103
|
+
constructor(options) {
|
|
93104
|
+
this.streamUrl = options.streamUrl;
|
|
93105
|
+
this.adaptor = options.adaptor;
|
|
93106
|
+
this.reconnectConfig = {
|
|
93107
|
+
...DEFAULT_RECONNECT_CONFIG,
|
|
93108
|
+
...options.reconnectConfig
|
|
93109
|
+
};
|
|
93110
|
+
this.heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
93111
|
+
this.debug = options.debug ?? false;
|
|
93112
|
+
this.client = new StreamsTransportClient({
|
|
93113
|
+
streamUrl: options.streamUrl,
|
|
93114
|
+
adapter: createNoopAdapter(),
|
|
93115
|
+
authProvider: normalizeAuthProvider(options.auth),
|
|
93116
|
+
fetchImpl: options.fetch ?? globalThis.fetch.bind(globalThis),
|
|
93117
|
+
reconnectConfig: this.reconnectConfig
|
|
93118
|
+
});
|
|
93119
|
+
}
|
|
93120
|
+
async join(params) {
|
|
93121
|
+
const existing = this.joinState;
|
|
93122
|
+
if (existing != null) try {
|
|
93123
|
+
await existing.started;
|
|
93124
|
+
return {
|
|
93125
|
+
ok: true,
|
|
93126
|
+
value: this.createSubscription(existing, params)
|
|
93127
|
+
};
|
|
93128
|
+
} catch (error2) {
|
|
93129
|
+
return {
|
|
93130
|
+
ok: false,
|
|
93131
|
+
error: toTransportError(error2)
|
|
93132
|
+
};
|
|
93133
|
+
}
|
|
93134
|
+
const state2 = this.createJoinState();
|
|
93135
|
+
state2.started = this.startJoin(state2);
|
|
93136
|
+
this.joinState = state2;
|
|
93137
|
+
try {
|
|
93138
|
+
await state2.started;
|
|
93139
|
+
return {
|
|
93140
|
+
ok: true,
|
|
93141
|
+
value: this.createSubscription(state2, params)
|
|
93142
|
+
};
|
|
93143
|
+
} catch (error2) {
|
|
93144
|
+
await this.disposeJoinState(state2);
|
|
93145
|
+
return {
|
|
93146
|
+
ok: false,
|
|
93147
|
+
error: toTransportError(error2)
|
|
93148
|
+
};
|
|
93149
|
+
}
|
|
93150
|
+
}
|
|
93151
|
+
rejoin() {
|
|
93152
|
+
const state2 = this.joinState;
|
|
93153
|
+
if (state2 == null || state2.closed) return;
|
|
93154
|
+
state2.retryAttempt = 0;
|
|
93155
|
+
state2.retrySleepController.abort();
|
|
93156
|
+
state2.retrySleepController = new AbortController();
|
|
93157
|
+
const previousController = state2.requestController;
|
|
93158
|
+
state2.requestController = new AbortController();
|
|
93159
|
+
previousController?.abort();
|
|
93160
|
+
if (state2.readStatus === "disconnected" || state2.readStatus === "error") {
|
|
93161
|
+
this.setReadStatus(state2, "reconnecting");
|
|
93162
|
+
this.runReadLoop(state2);
|
|
93163
|
+
}
|
|
93164
|
+
if (state2.writeStatus === "error") {
|
|
93165
|
+
this.setWriteStatus(state2, "reconnecting");
|
|
93166
|
+
this.flushPendingLocal(state2);
|
|
93167
|
+
}
|
|
93168
|
+
}
|
|
93169
|
+
async close() {
|
|
93170
|
+
const state2 = this.joinState;
|
|
93171
|
+
if (state2 != null) await this.disposeJoinState(state2);
|
|
93172
|
+
}
|
|
93173
|
+
createJoinState() {
|
|
93174
|
+
return {
|
|
93175
|
+
refCount: 0,
|
|
93176
|
+
closed: false,
|
|
93177
|
+
status: "connecting",
|
|
93178
|
+
readStatus: "connecting",
|
|
93179
|
+
writeStatus: "connecting",
|
|
93180
|
+
statusListeners: /* @__PURE__ */ new Set(),
|
|
93181
|
+
pendingLocal: new Deque(),
|
|
93182
|
+
syncWaiters: [],
|
|
93183
|
+
flushingLocal: false,
|
|
93184
|
+
retryAttempt: 0,
|
|
93185
|
+
retrySleepController: new AbortController(),
|
|
93186
|
+
firstOpenSettled: false,
|
|
93187
|
+
started: Promise.resolve()
|
|
93188
|
+
};
|
|
93189
|
+
}
|
|
93190
|
+
async startJoin(state2) {
|
|
93191
|
+
state2.requestController = new AbortController();
|
|
93192
|
+
const firstOpen = this.waitForFirstOpen(state2);
|
|
93193
|
+
this.runReadLoop(state2);
|
|
93194
|
+
await firstOpen;
|
|
93195
|
+
if (state2.closed) return;
|
|
93196
|
+
state2.unsubscribeLocal = this.adaptor.subscribeLocalUpdates((update2) => {
|
|
93197
|
+
this.enqueueLocal(state2, normalizeEphemeralUpdate(update2));
|
|
93198
|
+
});
|
|
93199
|
+
this.setWriteStatus(state2, "ok");
|
|
93200
|
+
const fullState = normalizeEphemeralUpdate(await this.adaptor.encodeAll());
|
|
93201
|
+
if (fullState != null) state2.pendingLocal.pushBack(fullState);
|
|
93202
|
+
this.startHeartbeat(state2);
|
|
93203
|
+
await this.flushPendingLocal(state2);
|
|
93204
|
+
if (state2.status === "error" || state2.status === "disconnected") throw new Error(`ephemeral stream failed to join: ${state2.status}`);
|
|
93205
|
+
}
|
|
93206
|
+
waitForFirstOpen(state2) {
|
|
93207
|
+
return new Promise((resolve2, reject) => {
|
|
93208
|
+
state2.firstOpenResolve = resolve2;
|
|
93209
|
+
state2.firstOpenReject = reject;
|
|
93210
|
+
});
|
|
93211
|
+
}
|
|
93212
|
+
createSubscription(state2, params) {
|
|
93213
|
+
state2.refCount += 1;
|
|
93214
|
+
const statusListener = params?.onStatusChange;
|
|
93215
|
+
if (statusListener != null) {
|
|
93216
|
+
state2.statusListeners.add(statusListener);
|
|
93217
|
+
statusListener(state2.status);
|
|
93218
|
+
}
|
|
93219
|
+
let unsubscribed = false;
|
|
93220
|
+
return {
|
|
93221
|
+
unsubscribe: () => {
|
|
93222
|
+
if (unsubscribed) return;
|
|
93223
|
+
unsubscribed = true;
|
|
93224
|
+
if (statusListener != null) state2.statusListeners.delete(statusListener);
|
|
93225
|
+
state2.refCount = Math.max(0, state2.refCount - 1);
|
|
93226
|
+
if (state2.refCount === 0 && !state2.closed) this.disposeJoinState(state2);
|
|
93227
|
+
},
|
|
93228
|
+
get connected() {
|
|
93229
|
+
return state2.status === "joined";
|
|
93230
|
+
},
|
|
93231
|
+
get status() {
|
|
93232
|
+
return state2.status;
|
|
93233
|
+
},
|
|
93234
|
+
onStatusChange: (listener) => this.onStatusChange(state2, listener),
|
|
93235
|
+
waitUntilSynced: async () => {
|
|
93236
|
+
if (state2.closed) throw closedSubscriptionError();
|
|
93237
|
+
const remaining = state2.pendingLocal.length;
|
|
93238
|
+
if (remaining === 0) return;
|
|
93239
|
+
return new Promise((resolve2, reject) => {
|
|
93240
|
+
state2.syncWaiters.push({
|
|
93241
|
+
remaining,
|
|
93242
|
+
resolve: resolve2,
|
|
93243
|
+
reject
|
|
93244
|
+
});
|
|
93245
|
+
});
|
|
93246
|
+
}
|
|
93247
|
+
};
|
|
93248
|
+
}
|
|
93249
|
+
onStatusChange(state2, listener) {
|
|
93250
|
+
state2.statusListeners.add(listener);
|
|
93251
|
+
listener(state2.status);
|
|
93252
|
+
return () => {
|
|
93253
|
+
state2.statusListeners.delete(listener);
|
|
93254
|
+
};
|
|
93255
|
+
}
|
|
93256
|
+
async disposeJoinState(state2) {
|
|
93257
|
+
if (state2.closed) return;
|
|
93258
|
+
state2.closed = true;
|
|
93259
|
+
state2.unsubscribeLocal?.();
|
|
93260
|
+
if (state2.heartbeatTimer != null) clearInterval(state2.heartbeatTimer);
|
|
93261
|
+
state2.requestController?.abort();
|
|
93262
|
+
state2.retrySleepController.abort();
|
|
93263
|
+
this.rejectFirstOpen(state2, closedSubscriptionError());
|
|
93264
|
+
this.rejectSyncWaiters(state2, closedSubscriptionError());
|
|
93265
|
+
if (this.joinState === state2) this.joinState = void 0;
|
|
93266
|
+
}
|
|
93267
|
+
resolveFirstOpen(state2) {
|
|
93268
|
+
if (state2.firstOpenSettled) return;
|
|
93269
|
+
state2.firstOpenSettled = true;
|
|
93270
|
+
state2.firstOpenResolve?.();
|
|
93271
|
+
}
|
|
93272
|
+
rejectFirstOpen(state2, error2) {
|
|
93273
|
+
if (state2.firstOpenSettled) return;
|
|
93274
|
+
state2.firstOpenSettled = true;
|
|
93275
|
+
state2.firstOpenReject?.(error2);
|
|
93276
|
+
}
|
|
93277
|
+
async runReadLoop(state2) {
|
|
93278
|
+
while (!state2.closed) {
|
|
93279
|
+
state2.requestController ??= new AbortController();
|
|
93280
|
+
const capturedController = state2.requestController;
|
|
93281
|
+
try {
|
|
93282
|
+
const result = await this.client.readEphemeralSse(async (bootstrap) => {
|
|
93283
|
+
if (bootstrap.byteLength > 0) await this.adaptor.applyRemoteUpdates([
|
|
93284
|
+
bootstrap
|
|
93285
|
+
]);
|
|
93286
|
+
this.setReadStatus(state2, "ok");
|
|
93287
|
+
this.resolveFirstOpen(state2);
|
|
93288
|
+
if (state2.unsubscribeLocal != null) this.enqueueFullState(state2);
|
|
93289
|
+
}, async (update2) => {
|
|
93290
|
+
if (update2.byteLength > 0) await this.adaptor.applyRemoteUpdates([
|
|
93291
|
+
update2
|
|
93292
|
+
]);
|
|
93293
|
+
}, capturedController.signal);
|
|
93294
|
+
if (state2.closed) return;
|
|
93295
|
+
if (result.kind === "ok") {
|
|
93296
|
+
state2.retryAttempt = 0;
|
|
93297
|
+
this.setReadStatus(state2, "reconnecting");
|
|
93298
|
+
await sleep$1(0);
|
|
93299
|
+
continue;
|
|
93300
|
+
}
|
|
93301
|
+
if (result.kind === "unsupported") throw new Error("ephemeral stream requires SSE support");
|
|
93302
|
+
if (result.kind === "not-found") throw new HttpError("ephemeral stream not found", 404);
|
|
93303
|
+
} catch (error2) {
|
|
93304
|
+
if (state2.closed) return;
|
|
93305
|
+
if (capturedController.signal.aborted) {
|
|
93306
|
+
state2.requestController = new AbortController();
|
|
93307
|
+
this.setReadStatus(state2, "reconnecting");
|
|
93308
|
+
continue;
|
|
93309
|
+
}
|
|
93310
|
+
const classified = classifyFetchError(error2);
|
|
93311
|
+
if (!classified.retriable || isAuthOrProtocolError(error2)) {
|
|
93312
|
+
this.logError("ephemeral live read failed", error2, {
|
|
93313
|
+
streamUrl: this.streamUrl,
|
|
93314
|
+
code: classified.code
|
|
93315
|
+
});
|
|
93316
|
+
this.setReadStatus(state2, "error");
|
|
93317
|
+
this.rejectFirstOpen(state2, toError$1(error2));
|
|
93318
|
+
return;
|
|
93319
|
+
}
|
|
93320
|
+
this.setReadStatus(state2, "reconnecting");
|
|
93321
|
+
if (state2.retryAttempt >= this.reconnectConfig.maxAttempts) {
|
|
93322
|
+
this.logError("ephemeral live reconnect exhausted", error2, {
|
|
93323
|
+
streamUrl: this.streamUrl,
|
|
93324
|
+
attempts: state2.retryAttempt
|
|
93325
|
+
});
|
|
93326
|
+
this.setReadStatus(state2, "disconnected");
|
|
93327
|
+
this.rejectFirstOpen(state2, toError$1(error2));
|
|
93328
|
+
return;
|
|
93329
|
+
}
|
|
93330
|
+
const delay2 = computeRetryDelay(state2.retryAttempt, this.reconnectConfig);
|
|
93331
|
+
state2.retryAttempt += 1;
|
|
93332
|
+
await sleep$1(delay2, state2.retrySleepController.signal);
|
|
93333
|
+
}
|
|
93334
|
+
}
|
|
93335
|
+
}
|
|
93336
|
+
startHeartbeat(state2) {
|
|
93337
|
+
if (this.heartbeatMs === false || this.heartbeatMs <= 0) return;
|
|
93338
|
+
state2.heartbeatTimer = setInterval(() => {
|
|
93339
|
+
this.enqueueFullState(state2);
|
|
93340
|
+
}, this.heartbeatMs);
|
|
93341
|
+
}
|
|
93342
|
+
async enqueueFullState(state2) {
|
|
93343
|
+
try {
|
|
93344
|
+
const update2 = await this.adaptor.encodeAll();
|
|
93345
|
+
this.enqueueLocal(state2, normalizeEphemeralUpdate(update2));
|
|
93346
|
+
} catch (error2) {
|
|
93347
|
+
this.logError("ephemeral encodeAll failed", error2, {
|
|
93348
|
+
streamUrl: this.streamUrl
|
|
93349
|
+
});
|
|
93350
|
+
this.setWriteStatus(state2, "error");
|
|
93351
|
+
}
|
|
93352
|
+
}
|
|
93353
|
+
enqueueLocal(state2, update2) {
|
|
93354
|
+
if (state2.closed || update2 == null || update2.byteLength === 0) return;
|
|
93355
|
+
state2.pendingLocal.pushBack(update2);
|
|
93356
|
+
this.flushPendingLocal(state2);
|
|
93357
|
+
}
|
|
93358
|
+
async flushPendingLocal(state2) {
|
|
93359
|
+
if (state2.flushingLocal || state2.closed) return;
|
|
93360
|
+
state2.flushingLocal = true;
|
|
93361
|
+
try {
|
|
93362
|
+
while (!state2.closed) {
|
|
93363
|
+
const update2 = state2.pendingLocal.peekFront();
|
|
93364
|
+
if (update2 == null) return;
|
|
93365
|
+
let attempt = 0;
|
|
93366
|
+
while (!state2.closed) try {
|
|
93367
|
+
await this.client.appendEphemeralUpdate(update2);
|
|
93368
|
+
state2.pendingLocal.popFront();
|
|
93369
|
+
this.notifySyncWaiters(state2, 1);
|
|
93370
|
+
this.setWriteStatus(state2, "ok");
|
|
93371
|
+
break;
|
|
93372
|
+
} catch (error2) {
|
|
93373
|
+
const classified = classifyFetchError(error2);
|
|
93374
|
+
if (!classified.retriable || isAuthOrProtocolError(error2)) {
|
|
93375
|
+
this.rejectSyncWaiters(state2, new Error(`ephemeral append failed: ${classified.code}`));
|
|
93376
|
+
this.setWriteStatus(state2, "error");
|
|
93377
|
+
return;
|
|
93378
|
+
}
|
|
93379
|
+
if (attempt >= this.reconnectConfig.maxAttempts) {
|
|
93380
|
+
this.rejectSyncWaiters(state2, new Error("ephemeral append retry budget exhausted"));
|
|
93381
|
+
this.setWriteStatus(state2, "error");
|
|
93382
|
+
return;
|
|
93383
|
+
}
|
|
93384
|
+
this.setWriteStatus(state2, "reconnecting");
|
|
93385
|
+
const delay2 = computeRetryDelay(attempt, this.reconnectConfig);
|
|
93386
|
+
attempt += 1;
|
|
93387
|
+
await sleep$1(delay2, state2.retrySleepController.signal);
|
|
93388
|
+
}
|
|
93389
|
+
}
|
|
93390
|
+
} finally {
|
|
93391
|
+
state2.flushingLocal = false;
|
|
93392
|
+
}
|
|
93393
|
+
}
|
|
93394
|
+
notifySyncWaiters(state2, flushedCount) {
|
|
93395
|
+
let i2 = 0;
|
|
93396
|
+
while (i2 < state2.syncWaiters.length) {
|
|
93397
|
+
const waiter = state2.syncWaiters[i2];
|
|
93398
|
+
waiter.remaining -= flushedCount;
|
|
93399
|
+
if (waiter.remaining <= 0) {
|
|
93400
|
+
state2.syncWaiters.splice(i2, 1);
|
|
93401
|
+
waiter.resolve();
|
|
93402
|
+
} else i2 += 1;
|
|
93403
|
+
}
|
|
93404
|
+
}
|
|
93405
|
+
rejectSyncWaiters(state2, error2) {
|
|
93406
|
+
for (const waiter of state2.syncWaiters.splice(0)) waiter.reject(error2);
|
|
93407
|
+
}
|
|
93408
|
+
setReadStatus(state2, readStatus) {
|
|
93409
|
+
if (state2.readStatus === readStatus) return;
|
|
93410
|
+
state2.readStatus = readStatus;
|
|
93411
|
+
this.recomputeStatus(state2);
|
|
93412
|
+
}
|
|
93413
|
+
setWriteStatus(state2, writeStatus) {
|
|
93414
|
+
if (state2.writeStatus === writeStatus) return;
|
|
93415
|
+
state2.writeStatus = writeStatus;
|
|
93416
|
+
this.recomputeStatus(state2);
|
|
93417
|
+
}
|
|
93418
|
+
recomputeStatus(state2) {
|
|
93419
|
+
const status = this.computeStatus(state2);
|
|
93420
|
+
if (state2.status === status) return;
|
|
93421
|
+
state2.status = status;
|
|
93422
|
+
for (const listener of state2.statusListeners) listener(status);
|
|
93423
|
+
}
|
|
93424
|
+
computeStatus(state2) {
|
|
93425
|
+
if (state2.readStatus === "error" || state2.writeStatus === "error") return "error";
|
|
93426
|
+
if (state2.readStatus === "disconnected") return "disconnected";
|
|
93427
|
+
if (state2.readStatus === "connecting" || state2.writeStatus === "connecting") return "connecting";
|
|
93428
|
+
if (state2.readStatus === "reconnecting" || state2.writeStatus === "reconnecting") return "reconnecting";
|
|
93429
|
+
return "joined";
|
|
93430
|
+
}
|
|
93431
|
+
logError(message, error2, detail) {
|
|
93432
|
+
if (!this.debug) return;
|
|
93433
|
+
console.error(`[streams-crdt] ${message}`, error2, detail);
|
|
93434
|
+
}
|
|
93435
|
+
};
|
|
92969
93436
|
function composeBeforeRemoteCursorSaveHooks(...hooks2) {
|
|
92970
93437
|
const active2 = hooks2.filter((hook) => hook != null);
|
|
92971
93438
|
if (active2.length === 0) return;
|
|
@@ -94494,6 +94961,15 @@ stream:${scope2.streamId}`;
|
|
|
94494
94961
|
}
|
|
94495
94962
|
};
|
|
94496
94963
|
}
|
|
94964
|
+
function EphemeralStoreAdaptor(store) {
|
|
94965
|
+
return {
|
|
94966
|
+
encodeAll: () => store.encodeAll(),
|
|
94967
|
+
applyRemoteUpdates: (updates) => {
|
|
94968
|
+
for (const update2 of updates) store.apply(update2);
|
|
94969
|
+
},
|
|
94970
|
+
subscribeLocalUpdates: (listener) => store.subscribeLocalUpdates(listener)
|
|
94971
|
+
};
|
|
94972
|
+
}
|
|
94497
94973
|
function isAfter(entry2, current2) {
|
|
94498
94974
|
return entry2.physicalTime > current2.physicalTime || entry2.physicalTime === current2.physicalTime && entry2.logicalCounter > current2.logicalCounter;
|
|
94499
94975
|
}
|
|
@@ -95362,6 +95838,133 @@ stream:${scope2.streamId}`;
|
|
|
95362
95838
|
}
|
|
95363
95839
|
}
|
|
95364
95840
|
};
|
|
95841
|
+
const formatErrorMessage = (error2, options = {}) => {
|
|
95842
|
+
if (error2 instanceof Error) {
|
|
95843
|
+
return options.includeStack ? error2.stack ?? error2.message : error2.message;
|
|
95844
|
+
}
|
|
95845
|
+
if (typeof error2 === "string") {
|
|
95846
|
+
return error2;
|
|
95847
|
+
}
|
|
95848
|
+
return inspect$2(error2, {
|
|
95849
|
+
depth: 5,
|
|
95850
|
+
maxArrayLength: 50,
|
|
95851
|
+
breakLength: 120
|
|
95852
|
+
});
|
|
95853
|
+
};
|
|
95854
|
+
class CliPresenceRuntime {
|
|
95855
|
+
constructor(options) {
|
|
95856
|
+
this.options = options;
|
|
95857
|
+
const durableStreamUrl = createStreamUrl({
|
|
95858
|
+
bucketId: LORO_STREAMS_BUCKET_ID,
|
|
95859
|
+
streamId: getLoroMetaStreamId(options.workspaceId),
|
|
95860
|
+
baseUrl: options.streamsBaseUrl
|
|
95861
|
+
});
|
|
95862
|
+
this.transport = new EphemeralStreamCrdt({
|
|
95863
|
+
streamUrl: toLodyPresenceStreamUrl(durableStreamUrl),
|
|
95864
|
+
auth: options.auth,
|
|
95865
|
+
adaptor: EphemeralStoreAdaptor(this.store),
|
|
95866
|
+
heartbeatMs: LODY_PRESENCE_STREAM_HEARTBEAT_MS
|
|
95867
|
+
});
|
|
95868
|
+
this.machineTimer = setInterval(() => {
|
|
95869
|
+
this.writeMachineHeartbeat();
|
|
95870
|
+
}, LODY_PRESENCE_HEARTBEAT_MS);
|
|
95871
|
+
this.machineTimer.unref?.();
|
|
95872
|
+
}
|
|
95873
|
+
instanceId = v4();
|
|
95874
|
+
store = new EphemeralStore(LODY_PRESENCE_TTL_MS);
|
|
95875
|
+
transport;
|
|
95876
|
+
machineTimer;
|
|
95877
|
+
subscription = null;
|
|
95878
|
+
machineId = null;
|
|
95879
|
+
machineKey = null;
|
|
95880
|
+
sessionKeys = /* @__PURE__ */ new Map();
|
|
95881
|
+
started = false;
|
|
95882
|
+
stopped = false;
|
|
95883
|
+
start() {
|
|
95884
|
+
if (this.started || this.stopped) return;
|
|
95885
|
+
this.started = true;
|
|
95886
|
+
void this.transport.join({
|
|
95887
|
+
onStatusChange: (status) => {
|
|
95888
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Loro presence room status: ${status}`);
|
|
95889
|
+
}
|
|
95890
|
+
}).then((result) => {
|
|
95891
|
+
if (this.stopped) {
|
|
95892
|
+
if (result.ok) result.value.unsubscribe();
|
|
95893
|
+
return;
|
|
95894
|
+
}
|
|
95895
|
+
if (result.ok) {
|
|
95896
|
+
this.subscription = result.value;
|
|
95897
|
+
return;
|
|
95898
|
+
}
|
|
95899
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Failed to join Loro presence room: ${formatErrorMessage(result.error)}`);
|
|
95900
|
+
}).catch((error2) => {
|
|
95901
|
+
if (this.stopped) return;
|
|
95902
|
+
this.options.logger.debug(`[${this.options.workspaceId}] Failed to start Loro presence room: ${formatErrorMessage(error2)}`);
|
|
95903
|
+
});
|
|
95904
|
+
}
|
|
95905
|
+
async stop() {
|
|
95906
|
+
if (this.stopped) return;
|
|
95907
|
+
this.stopped = true;
|
|
95908
|
+
clearInterval(this.machineTimer);
|
|
95909
|
+
this.clearMachinePresence();
|
|
95910
|
+
for (const sessionId of Array.from(this.sessionKeys.keys())) {
|
|
95911
|
+
this.clearSessionPresence(sessionId);
|
|
95912
|
+
}
|
|
95913
|
+
this.subscription?.unsubscribe();
|
|
95914
|
+
this.subscription = null;
|
|
95915
|
+
await this.transport.close();
|
|
95916
|
+
this.store.destroy();
|
|
95917
|
+
}
|
|
95918
|
+
setMachineOnline(machineId) {
|
|
95919
|
+
if (this.stopped) return;
|
|
95920
|
+
this.machineId = machineId;
|
|
95921
|
+
this.machineKey = getLodyMachinePresenceKey(machineId, this.instanceId);
|
|
95922
|
+
this.writeMachineHeartbeat();
|
|
95923
|
+
}
|
|
95924
|
+
writeMachineHeartbeat() {
|
|
95925
|
+
if (this.stopped || !this.machineId || !this.machineKey) return;
|
|
95926
|
+
const state2 = {
|
|
95927
|
+
kind: "machine",
|
|
95928
|
+
machineId: this.machineId,
|
|
95929
|
+
instanceId: this.instanceId,
|
|
95930
|
+
updatedAt: getServerNow()
|
|
95931
|
+
};
|
|
95932
|
+
this.store.set(this.machineKey, state2);
|
|
95933
|
+
}
|
|
95934
|
+
setSessionPresence(args2) {
|
|
95935
|
+
if (this.stopped) return;
|
|
95936
|
+
if (!args2.status || args2.status.type === "idle") {
|
|
95937
|
+
this.clearSessionPresence(args2.sessionId);
|
|
95938
|
+
return;
|
|
95939
|
+
}
|
|
95940
|
+
if (!args2.machineId) {
|
|
95941
|
+
return;
|
|
95942
|
+
}
|
|
95943
|
+
const key2 = getLodySessionPresenceKey(args2.sessionId, this.instanceId);
|
|
95944
|
+
this.sessionKeys.set(args2.sessionId, key2);
|
|
95945
|
+
const state2 = {
|
|
95946
|
+
kind: "session",
|
|
95947
|
+
sessionId: args2.sessionId,
|
|
95948
|
+
machineId: args2.machineId,
|
|
95949
|
+
instanceId: this.instanceId,
|
|
95950
|
+
status: args2.status,
|
|
95951
|
+
updatedAt: getServerNow()
|
|
95952
|
+
};
|
|
95953
|
+
this.store.set(key2, state2);
|
|
95954
|
+
}
|
|
95955
|
+
clearSessionPresence(sessionId) {
|
|
95956
|
+
const key2 = this.sessionKeys.get(sessionId);
|
|
95957
|
+
if (!key2) return;
|
|
95958
|
+
this.sessionKeys.delete(sessionId);
|
|
95959
|
+
this.store.delete(key2);
|
|
95960
|
+
}
|
|
95961
|
+
clearMachinePresence() {
|
|
95962
|
+
if (!this.machineKey) return;
|
|
95963
|
+
this.store.delete(this.machineKey);
|
|
95964
|
+
this.machineKey = null;
|
|
95965
|
+
this.machineId = null;
|
|
95966
|
+
}
|
|
95967
|
+
}
|
|
95365
95968
|
const findLatestUserHistoryEntry = (history) => {
|
|
95366
95969
|
for (let i2 = history.length - 1; i2 >= 0; i2--) {
|
|
95367
95970
|
const entry2 = history[i2];
|
|
@@ -112520,19 +113123,6 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
112520
113123
|
const shutdown = shutdown$1;
|
|
112521
113124
|
const unsafeOffer = unsafeOffer$1;
|
|
112522
113125
|
const take$1 = take$2;
|
|
112523
|
-
const formatErrorMessage = (error2, options = {}) => {
|
|
112524
|
-
if (error2 instanceof Error) {
|
|
112525
|
-
return options.includeStack ? error2.stack ?? error2.message : error2.message;
|
|
112526
|
-
}
|
|
112527
|
-
if (typeof error2 === "string") {
|
|
112528
|
-
return error2;
|
|
112529
|
-
}
|
|
112530
|
-
return inspect$2(error2, {
|
|
112531
|
-
depth: 5,
|
|
112532
|
-
maxArrayLength: 50,
|
|
112533
|
-
breakLength: 120
|
|
112534
|
-
});
|
|
112535
|
-
};
|
|
112536
113126
|
const isRecoverableMetaRoomStatus = (status) => status === "disconnected" || status === "error";
|
|
112537
113127
|
class LoroConnectionRecoveryController {
|
|
112538
113128
|
recoveryEvents = runSync(unbounded());
|
|
@@ -113640,12 +114230,13 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113640
114230
|
};
|
|
113641
114231
|
};
|
|
113642
114232
|
class LoroDocumentManager {
|
|
113643
|
-
constructor(repo, workspaceId, userId, metaSub, logger2, detachTransportLogger, initialTransportStatus = "disconnected", initialMetaSyncPromise = Promise.resolve(false), initialMetaSyncCompleted = false) {
|
|
114233
|
+
constructor(repo, workspaceId, userId, metaSub, logger2, detachTransportLogger, initialTransportStatus = "disconnected", initialMetaSyncPromise = Promise.resolve(false), initialMetaSyncCompleted = false, presenceRuntime = null) {
|
|
113644
114234
|
this.repo = repo;
|
|
113645
114235
|
this.workspaceId = workspaceId;
|
|
113646
114236
|
this.userId = userId;
|
|
113647
114237
|
this.logger = logger2;
|
|
113648
114238
|
this.detachTransportLogger = detachTransportLogger;
|
|
114239
|
+
this.presenceRuntime = presenceRuntime;
|
|
113649
114240
|
this.initialMetaSyncPromise = initialMetaSyncPromise.then((completed) => {
|
|
113650
114241
|
if (completed) {
|
|
113651
114242
|
this.initialMetaSyncCompleted = true;
|
|
@@ -113673,6 +114264,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113673
114264
|
connectionRecovery;
|
|
113674
114265
|
initialMetaSyncCompleted = false;
|
|
113675
114266
|
initialMetaSyncPromise;
|
|
114267
|
+
presenceRuntime;
|
|
113676
114268
|
static async create(workspaceId, userId, token2, logger2) {
|
|
113677
114269
|
const authBaseUrl = LODY_AUTH_SITE_URL ? normalizeBaseUrl(LODY_AUTH_SITE_URL) : LODY_AUTH_URL ? deriveConvexSiteUrl(LODY_AUTH_URL) : null;
|
|
113678
114270
|
if (!authBaseUrl) {
|
|
@@ -113770,7 +114362,14 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113770
114362
|
} else {
|
|
113771
114363
|
initialMetaSync.resolve(false);
|
|
113772
114364
|
}
|
|
113773
|
-
|
|
114365
|
+
const presenceRuntime = new CliPresenceRuntime({
|
|
114366
|
+
workspaceId,
|
|
114367
|
+
streamsBaseUrl: streamsTokenProvider.getGatewayBaseUrl() ?? streamsBaseUrl,
|
|
114368
|
+
auth: streamsTokenProvider.createAuthCallback(),
|
|
114369
|
+
logger: logger2
|
|
114370
|
+
});
|
|
114371
|
+
presenceRuntime.start();
|
|
114372
|
+
manager = new LoroDocumentManager(repo, workspaceId, userId, metaSub, logger2, detachTransportLogger, transportAdapter.getStatus?.() ?? "disconnected", initialMetaSync.promise, initialMetaSyncCompleted, presenceRuntime);
|
|
113774
114373
|
return manager;
|
|
113775
114374
|
}
|
|
113776
114375
|
setTransportStatus(status) {
|
|
@@ -113840,7 +114439,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113840
114439
|
return pending2;
|
|
113841
114440
|
}
|
|
113842
114441
|
const initPromise2 = (async () => {
|
|
113843
|
-
const sessionDoc = new SessionDocument(this.repo, sessionId, this.logger);
|
|
114442
|
+
const sessionDoc = new SessionDocument(this.repo, sessionId, this.logger, this.presenceRuntime);
|
|
113844
114443
|
await sessionDoc.init();
|
|
113845
114444
|
if (sessionDoc.isDestroyed) {
|
|
113846
114445
|
throw new Error(`Session doc ${sessionId} was destroyed during init`);
|
|
@@ -113856,7 +114455,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113856
114455
|
}
|
|
113857
114456
|
}
|
|
113858
114457
|
async getSessionHistorySnapshot(sessionId) {
|
|
113859
|
-
const sessionDoc = new SessionDocument(this.repo, sessionId, this.logger);
|
|
114458
|
+
const sessionDoc = new SessionDocument(this.repo, sessionId, this.logger, null);
|
|
113860
114459
|
await sessionDoc.init({
|
|
113861
114460
|
skipAutoRead: true
|
|
113862
114461
|
});
|
|
@@ -113887,6 +114486,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113887
114486
|
sessionMeta.title = sanitizedTitle;
|
|
113888
114487
|
}
|
|
113889
114488
|
await this.repo.upsertDocMeta(sessionDoc.roomId, sessionMeta);
|
|
114489
|
+
this.presenceRuntime?.setSessionPresence({
|
|
114490
|
+
sessionId,
|
|
114491
|
+
machineId: sessionMeta.machineId,
|
|
114492
|
+
status: sessionMeta.status
|
|
114493
|
+
});
|
|
113890
114494
|
return sessionId;
|
|
113891
114495
|
}
|
|
113892
114496
|
async getOrOpenSessionCode(options) {
|
|
@@ -113965,6 +114569,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113965
114569
|
...machine,
|
|
113966
114570
|
lastSeen: getServerNow()
|
|
113967
114571
|
});
|
|
114572
|
+
this.presenceRuntime?.setMachineOnline(machineId);
|
|
113968
114573
|
}
|
|
113969
114574
|
async updateRateLimits(machineId, cliType, limits) {
|
|
113970
114575
|
if (!this.machine) {
|
|
@@ -113994,6 +114599,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
113994
114599
|
return;
|
|
113995
114600
|
}
|
|
113996
114601
|
await this.machine.sendHeartbeat();
|
|
114602
|
+
this.presenceRuntime?.writeMachineHeartbeat();
|
|
113997
114603
|
}
|
|
113998
114604
|
async restoreMachineDocument(machineId) {
|
|
113999
114605
|
const machineRoomId = getMachineRoomId(machineId);
|
|
@@ -114019,6 +114625,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114019
114625
|
this.logger.debug(`Started watching document existence for machine ${machineId}`);
|
|
114020
114626
|
}
|
|
114021
114627
|
async cleanUp(options = {}) {
|
|
114628
|
+
try {
|
|
114629
|
+
await this.presenceRuntime?.stop();
|
|
114630
|
+
} catch (error2) {
|
|
114631
|
+
this.logger.debug(`[${this.workspaceId}] Failed to stop Loro presence runtime: ${formatErrorMessage(error2)}`);
|
|
114632
|
+
}
|
|
114022
114633
|
await this.connectionRecovery.cleanUp();
|
|
114023
114634
|
for (const [sessionId, pending2] of this.pendingSessionDocs) {
|
|
114024
114635
|
try {
|
|
@@ -114081,10 +114692,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114081
114692
|
}
|
|
114082
114693
|
}
|
|
114083
114694
|
class SessionDocument {
|
|
114084
|
-
constructor(repo, sessionId, logger2 = getLogger("loro")) {
|
|
114695
|
+
constructor(repo, sessionId, logger2 = getLogger("loro"), presenceRuntime = null) {
|
|
114085
114696
|
this.repo = repo;
|
|
114086
114697
|
this.sessionId = sessionId;
|
|
114087
114698
|
this.logger = logger2;
|
|
114699
|
+
this.presenceRuntime = presenceRuntime;
|
|
114088
114700
|
this.roomId = getSessionRoomId(this.sessionId);
|
|
114089
114701
|
}
|
|
114090
114702
|
mirror = null;
|
|
@@ -114510,9 +115122,15 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114510
115122
|
}
|
|
114511
115123
|
const current2 = await this.repo.getDocMeta(this.roomId);
|
|
114512
115124
|
if (isLoroRepoDocDeleted(current2)) return;
|
|
115125
|
+
const currentMeta = current2?.meta;
|
|
114513
115126
|
await this.repo.upsertDocMeta(this.roomId, {
|
|
114514
115127
|
lastRunningSeen: getServerNow()
|
|
114515
115128
|
});
|
|
115129
|
+
this.presenceRuntime?.setSessionPresence({
|
|
115130
|
+
sessionId: this.sessionId,
|
|
115131
|
+
machineId: currentMeta?.machineId,
|
|
115132
|
+
status: currentMeta?.status
|
|
115133
|
+
});
|
|
114516
115134
|
}
|
|
114517
115135
|
async getStatus() {
|
|
114518
115136
|
if (!this.mirror) {
|
|
@@ -114538,6 +115156,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114538
115156
|
this.logger.debug(`[${this.sessionId}] setStatus: doc deleted, skipping`);
|
|
114539
115157
|
return;
|
|
114540
115158
|
}
|
|
115159
|
+
const currentMeta = current2?.meta;
|
|
114541
115160
|
this.logger.debug(`[${this.sessionId}] setStatus: calling upsertDocMeta (status.type=${status.type})`);
|
|
114542
115161
|
const patch2 = {
|
|
114543
115162
|
status
|
|
@@ -114546,6 +115165,11 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114546
115165
|
patch2.lastRunningSeen = getServerNow();
|
|
114547
115166
|
}
|
|
114548
115167
|
await withSlowOperationWarning(this.repo.upsertDocMeta(this.roomId, patch2), this.logger, `repo.upsertDocMeta(status=${status.type})`, this.sessionId);
|
|
115168
|
+
this.presenceRuntime?.setSessionPresence({
|
|
115169
|
+
sessionId: this.sessionId,
|
|
115170
|
+
machineId: currentMeta?.machineId,
|
|
115171
|
+
status
|
|
115172
|
+
});
|
|
114549
115173
|
this.logger.debug(`[${this.sessionId}] setStatus: upsertDocMeta complete`);
|
|
114550
115174
|
}
|
|
114551
115175
|
async getHistory() {
|
|
@@ -114789,7 +115413,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
|
|
|
114789
115413
|
this.historyAutoReadHandle?.dispose();
|
|
114790
115414
|
this.historyAutoReadHandle = null;
|
|
114791
115415
|
const status = await this.getStatus();
|
|
114792
|
-
if (!options.preserveStatus && (status?.type === "running" || status?.type === "requestPermission")) {
|
|
115416
|
+
if (!options.preserveStatus && (status?.type === "running" || status?.type === "requestPermission" || status?.type === "initializing")) {
|
|
114793
115417
|
await this.setStatus(SessionStatusFactory.idle());
|
|
114794
115418
|
}
|
|
114795
115419
|
await this.repo.unloadDoc(this.roomId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lody",
|
|
3
|
-
"version": "0.46.
|
|
3
|
+
"version": "0.46.2-next.1",
|
|
4
4
|
"description": "Lody Agent CLI tool for managing remote command execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"winston-transport": "^4.7.1",
|
|
73
73
|
"ws": "^8.18.3",
|
|
74
74
|
"zod": "^4.1.5",
|
|
75
|
-
"@lody/cli-supervisor": "0.0.1",
|
|
76
75
|
"@lody/convex": "0.0.1",
|
|
76
|
+
"@lody/cli-supervisor": "0.0.1",
|
|
77
77
|
"@lody/loro-streams-rpc": "0.0.1",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
78
|
+
"@lody/shared": "0.0.1",
|
|
79
|
+
"loro-code": "0.0.1"
|
|
80
80
|
},
|
|
81
81
|
"files": [
|
|
82
82
|
"dist",
|