paseo-room 0.15.2 → 0.16.0
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 +65 -72
- package/dist/index.js.map +1 -1
- package/dist/runtime-plugin/client/model.ts +3 -1
- package/dist/runtime-plugin/client/views.tsx +10 -3
- package/dist/runtime-plugin/server/controller.ts +9 -7
- package/dist/runtime-plugin/server/correlations.ts +11 -1
- package/dist/runtime-plugin/server/domain/state.ts +27 -6
- package/dist/runtime-plugin/server/handlers/actions.ts +4 -5
- package/dist/runtime-plugin/server/handlers/peer.ts +31 -7
- package/dist/runtime-plugin/server/projection.ts +70 -0
- package/dist/runtime-plugin/server/rpc.ts +16 -6
- package/dist/runtime-plugin/server/spool.ts +93 -7
- package/dist/runtime-plugin/server/store/project.ts +50 -54
- package/dist/runtime-plugin/server/store/publish.ts +1 -7
- package/dist/runtime-plugin/shared/limits.ts +8 -0
- package/dist/runtime-plugin/shared/rpc-contracts.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "paseo-room",
|
|
18
|
-
version: "0.
|
|
18
|
+
version: "0.16.0",
|
|
19
19
|
description: "One CLI that configures Codex, Claude Code, and Pi role homes in $HOME and registers them with your local Paseo daemon.",
|
|
20
20
|
keywords: [
|
|
21
21
|
"paseo",
|
|
@@ -3182,14 +3182,20 @@ function applyEvent(state, event) {
|
|
|
3182
3182
|
return;
|
|
3183
3183
|
}
|
|
3184
3184
|
}
|
|
3185
|
-
function
|
|
3186
|
-
|
|
3187
|
-
|
|
3185
|
+
function foldInto(state, events, from = 0) {
|
|
3186
|
+
for (let index = from; index < events.length; index += 1) {
|
|
3187
|
+
const event = events[index];
|
|
3188
|
+
if (event === void 0) continue;
|
|
3188
3189
|
const problem = checkEvent(state, event);
|
|
3189
|
-
if (problem !== void 0) return {
|
|
3190
|
+
if (problem !== void 0) return { eventId: event.id, type: event.type, message: problem };
|
|
3190
3191
|
applyEvent(state, event);
|
|
3191
3192
|
}
|
|
3192
|
-
return
|
|
3193
|
+
return void 0;
|
|
3194
|
+
}
|
|
3195
|
+
function project(projectId, events) {
|
|
3196
|
+
const state = emptyProjectState(projectId);
|
|
3197
|
+
const violation = foldInto(state, events);
|
|
3198
|
+
return { state, violations: violation === void 0 ? [] : [violation] };
|
|
3193
3199
|
}
|
|
3194
3200
|
function reclaimCheck(state, assignmentId) {
|
|
3195
3201
|
const refused = (code2, message) => ({ ok: false, code: code2, message });
|
|
@@ -3779,7 +3785,7 @@ var init_schema = __esm({
|
|
|
3779
3785
|
|
|
3780
3786
|
// src/runtime-plugin/server/store/publish.ts
|
|
3781
3787
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3782
|
-
import { chmod as chmod2, link, lstat as lstat4, mkdir as mkdir2, open,
|
|
3788
|
+
import { chmod as chmod2, link, lstat as lstat4, mkdir as mkdir2, open, unlink as unlink2 } from "fs/promises";
|
|
3783
3789
|
import { join as join13 } from "path";
|
|
3784
3790
|
function code(error) {
|
|
3785
3791
|
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
@@ -3847,10 +3853,6 @@ async function publishOnce(directory, name, content, hook) {
|
|
|
3847
3853
|
if (path === "") throw new AlreadyPublishedError(join13(directory, name));
|
|
3848
3854
|
return path;
|
|
3849
3855
|
}
|
|
3850
|
-
async function staleTemporaries(directory) {
|
|
3851
|
-
const names = await readdir3(directory);
|
|
3852
|
-
return names.filter((name) => name.startsWith(TEMPORARY_PREFIX)).sort();
|
|
3853
|
-
}
|
|
3854
3856
|
var TEMPORARY_PREFIX, PRIVATE_DIR_MODE, PRIVATE_FILE_MODE, PublishUnsupportedError, AlreadyPublishedError, UNSUPPORTED;
|
|
3855
3857
|
var init_publish = __esm({
|
|
3856
3858
|
"src/runtime-plugin/server/store/publish.ts"() {
|
|
@@ -3878,49 +3880,43 @@ var init_publish = __esm({
|
|
|
3878
3880
|
|
|
3879
3881
|
// src/runtime-plugin/server/store/project.ts
|
|
3880
3882
|
import { randomBytes as randomBytes3, randomUUID } from "crypto";
|
|
3881
|
-
import { mkdir as mkdir3, readdir as
|
|
3883
|
+
import { mkdir as mkdir3, readdir as readdir3, readFile as readFile4, rename as rename2, rm as rm3, writeFile as writeFile2 } from "fs/promises";
|
|
3882
3884
|
import { basename as basename2, join as join14 } from "path";
|
|
3883
3885
|
import { z as z12 } from "zod";
|
|
3884
3886
|
function runtimeRoot(roomHome) {
|
|
3885
3887
|
return join14(roomHome, "runtime", RUNTIME_LAYOUT_VERSION);
|
|
3886
3888
|
}
|
|
3887
|
-
async function
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
files.set(name, { value: JSON.parse(await readFile4(join14(directory, name), "utf8")) });
|
|
3894
|
-
} catch (error) {
|
|
3895
|
-
files.set(name, { error: error instanceof Error ? error.message : String(error) });
|
|
3896
|
-
}
|
|
3897
|
-
}
|
|
3898
|
-
};
|
|
3899
|
-
await Promise.all(Array.from({ length: Math.min(REPLAY_READS_IN_FLIGHT, names.length) }, reader));
|
|
3900
|
-
return files;
|
|
3901
|
-
}
|
|
3902
|
-
async function readEventFiles(directory, names) {
|
|
3903
|
-
let known = READS.get(directory);
|
|
3904
|
-
if (known === void 0) {
|
|
3905
|
-
known = /* @__PURE__ */ new Map();
|
|
3906
|
-
READS.set(directory, known);
|
|
3889
|
+
async function readChecked(path) {
|
|
3890
|
+
try {
|
|
3891
|
+
const read = readEvent(JSON.parse(await readFile4(path, "utf8")));
|
|
3892
|
+
return read.ok ? { event: read.event } : { reason: read.reason, detail: read.detail };
|
|
3893
|
+
} catch (error) {
|
|
3894
|
+
return { reason: "unreadable", detail: error instanceof Error ? error.message : String(error) };
|
|
3907
3895
|
}
|
|
3896
|
+
}
|
|
3897
|
+
async function checkedFiles(directory, names) {
|
|
3898
|
+
const known = CHECKED.get(directory) ?? /* @__PURE__ */ new Map();
|
|
3899
|
+
CHECKED.set(directory, known);
|
|
3908
3900
|
const present = new Set(names);
|
|
3909
3901
|
for (const name of known.keys()) if (!present.has(name)) known.delete(name);
|
|
3910
|
-
const missing = names.filter((name) => !known.has(name))
|
|
3911
|
-
|
|
3912
|
-
const
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
}
|
|
3920
|
-
}
|
|
3921
|
-
const
|
|
3922
|
-
|
|
3923
|
-
|
|
3902
|
+
const missing = names.filter((name) => !known.has(name)).map((name) => {
|
|
3903
|
+
let settle;
|
|
3904
|
+
const result = new Promise((resolve5) => {
|
|
3905
|
+
settle = resolve5;
|
|
3906
|
+
});
|
|
3907
|
+
known.set(name, result);
|
|
3908
|
+
void result.then((found) => {
|
|
3909
|
+
if ("reason" in found && found.reason === "unreadable" && known.get(name) === result) known.delete(name);
|
|
3910
|
+
});
|
|
3911
|
+
return { name, settle };
|
|
3912
|
+
});
|
|
3913
|
+
const queue = missing.values();
|
|
3914
|
+
const reader = async () => {
|
|
3915
|
+
for (const { name, settle } of queue) settle(await readChecked(join14(directory, name)));
|
|
3916
|
+
};
|
|
3917
|
+
void Promise.all(Array.from({ length: Math.min(REPLAY_READS_IN_FLIGHT, missing.length) }, reader));
|
|
3918
|
+
const results = await Promise.all(names.map((name) => known.get(name) ?? Promise.resolve(NOT_READ)));
|
|
3919
|
+
return new Map(names.map((name, index) => [name, results[index] ?? NOT_READ]));
|
|
3924
3920
|
}
|
|
3925
3921
|
function eventName(sequence) {
|
|
3926
3922
|
return `${String(sequence).padStart(12, "0")}.json`;
|
|
@@ -3929,7 +3925,7 @@ function slug(root) {
|
|
|
3929
3925
|
const name = basename2(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
|
|
3930
3926
|
return name === "" ? "project" : name;
|
|
3931
3927
|
}
|
|
3932
|
-
var RUNTIME_LAYOUT_VERSION, EVENT_FILE, MAX_SEQUENCE_ATTEMPTS, REPLAY_READS_IN_FLIGHT, metaSchema,
|
|
3928
|
+
var RUNTIME_LAYOUT_VERSION, EVENT_FILE, MAX_SEQUENCE_ATTEMPTS, REPLAY_READS_IN_FLIGHT, metaSchema, NOT_READ, CHECKED, ProjectStore;
|
|
3933
3929
|
var init_project = __esm({
|
|
3934
3930
|
"src/runtime-plugin/server/store/project.ts"() {
|
|
3935
3931
|
"use strict";
|
|
@@ -3946,7 +3942,8 @@ var init_project = __esm({
|
|
|
3946
3942
|
canonicalRoot: z12.string().min(1),
|
|
3947
3943
|
gitCommonDir: z12.string().min(1)
|
|
3948
3944
|
});
|
|
3949
|
-
|
|
3945
|
+
NOT_READ = { reason: "unreadable", detail: "The event file was not read." };
|
|
3946
|
+
CHECKED = /* @__PURE__ */ new Map();
|
|
3950
3947
|
ProjectStore = class _ProjectStore {
|
|
3951
3948
|
constructor(directory, meta, now) {
|
|
3952
3949
|
this.directory = directory;
|
|
@@ -4003,7 +4000,7 @@ var init_project = __esm({
|
|
|
4003
4000
|
const projects = join14(root, "projects");
|
|
4004
4001
|
let names;
|
|
4005
4002
|
try {
|
|
4006
|
-
names = await
|
|
4003
|
+
names = await readdir3(projects);
|
|
4007
4004
|
} catch {
|
|
4008
4005
|
return [];
|
|
4009
4006
|
}
|
|
@@ -4018,29 +4015,25 @@ var init_project = __esm({
|
|
|
4018
4015
|
}
|
|
4019
4016
|
/** Reads every event strictly. Any unreadable file pauses the project; nothing is moved. */
|
|
4020
4017
|
async replay() {
|
|
4021
|
-
const names = (await
|
|
4022
|
-
const
|
|
4018
|
+
const names = (await readdir3(this.eventsDirectory)).sort();
|
|
4019
|
+
const eventNames = names.filter((name) => EVENT_FILE.test(name));
|
|
4020
|
+
const files = await checkedFiles(this.eventsDirectory, eventNames);
|
|
4023
4021
|
const problems = [];
|
|
4024
4022
|
const events = [];
|
|
4025
4023
|
const ids = /* @__PURE__ */ new Map();
|
|
4026
4024
|
for (const name of names) {
|
|
4027
|
-
if (name.startsWith(
|
|
4025
|
+
if (name.startsWith(TEMPORARY_PREFIX)) continue;
|
|
4028
4026
|
const match = EVENT_FILE.exec(name);
|
|
4029
4027
|
if (!match) {
|
|
4030
4028
|
problems.push({ file: name, reason: "unexpected-file", detail: "Not a runtime event file name." });
|
|
4031
4029
|
continue;
|
|
4032
4030
|
}
|
|
4033
|
-
const file = files.get(name) ??
|
|
4034
|
-
if ("
|
|
4035
|
-
problems.push({ file: name, reason:
|
|
4036
|
-
continue;
|
|
4037
|
-
}
|
|
4038
|
-
const read = readEvent(file.value);
|
|
4039
|
-
if (!read.ok) {
|
|
4040
|
-
problems.push({ file: name, reason: read.reason, detail: read.detail });
|
|
4031
|
+
const file = files.get(name) ?? NOT_READ;
|
|
4032
|
+
if ("reason" in file) {
|
|
4033
|
+
problems.push({ file: name, reason: file.reason, detail: file.detail });
|
|
4041
4034
|
continue;
|
|
4042
4035
|
}
|
|
4043
|
-
const event =
|
|
4036
|
+
const event = file.event;
|
|
4044
4037
|
if (event.sequence !== Number(match[1])) {
|
|
4045
4038
|
problems.push({ file: name, reason: "sequence-mismatch", detail: `File name does not match sequence ${String(event.sequence)}.` });
|
|
4046
4039
|
continue;
|
|
@@ -4063,14 +4056,14 @@ var init_project = __esm({
|
|
|
4063
4056
|
for (; expected < event.sequence; expected += 1) gaps.push(expected);
|
|
4064
4057
|
expected = event.sequence + 1;
|
|
4065
4058
|
}
|
|
4066
|
-
const highest =
|
|
4067
|
-
this.lastSequence = Math.max(this.lastSequence,
|
|
4059
|
+
const highest = Number(EVENT_FILE.exec(eventNames.at(-1) ?? "")?.[1] ?? 0);
|
|
4060
|
+
this.lastSequence = Math.max(this.lastSequence, highest);
|
|
4068
4061
|
return {
|
|
4069
4062
|
status: problems.length === 0 ? "ok" : "paused",
|
|
4070
4063
|
events,
|
|
4071
4064
|
problems,
|
|
4072
4065
|
gaps,
|
|
4073
|
-
staleTemporaries:
|
|
4066
|
+
staleTemporaries: names.filter((name) => name.startsWith(TEMPORARY_PREFIX))
|
|
4074
4067
|
};
|
|
4075
4068
|
}
|
|
4076
4069
|
/**
|
|
@@ -4123,7 +4116,7 @@ var init_project = __esm({
|
|
|
4123
4116
|
}
|
|
4124
4117
|
/** Moves one named event file aside. Only an explicit operator action calls this. */
|
|
4125
4118
|
async quarantine(file) {
|
|
4126
|
-
if (!EVENT_FILE.test(file) && !file.startsWith(
|
|
4119
|
+
if (!EVENT_FILE.test(file) && !file.startsWith(TEMPORARY_PREFIX)) throw new Error(`${file} is not a runtime event file.`);
|
|
4127
4120
|
await ensurePrivateDirectory(this.quarantineDirectory);
|
|
4128
4121
|
await rename2(join14(this.eventsDirectory, file), join14(this.quarantineDirectory, file));
|
|
4129
4122
|
}
|
|
@@ -4133,11 +4126,11 @@ var init_project = __esm({
|
|
|
4133
4126
|
|
|
4134
4127
|
// src/runtime-state.ts
|
|
4135
4128
|
import { existsSync } from "fs";
|
|
4136
|
-
import { readdir as
|
|
4129
|
+
import { readdir as readdir4 } from "fs/promises";
|
|
4137
4130
|
import { join as join15 } from "path";
|
|
4138
4131
|
async function inspectRuntimeState(roomHome) {
|
|
4139
4132
|
const root = runtimeRoot(roomHome);
|
|
4140
|
-
const names = await
|
|
4133
|
+
const names = await readdir4(join15(root, "projects")).catch(() => []);
|
|
4141
4134
|
const blockers = [];
|
|
4142
4135
|
const stores = [];
|
|
4143
4136
|
let retained = 0;
|
|
@@ -4894,12 +4887,12 @@ init_layout();
|
|
|
4894
4887
|
init_result();
|
|
4895
4888
|
init_schema();
|
|
4896
4889
|
init_project();
|
|
4897
|
-
import { copyFile, mkdir as mkdir4, readdir as
|
|
4890
|
+
import { copyFile, mkdir as mkdir4, readdir as readdir5, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
4898
4891
|
import { join as join17, resolve as resolve4 } from "path";
|
|
4899
4892
|
var LIMITATION = "Briefs, answers, reports and gate commands are stored as written and cannot be proven secret-free; review the export before sharing it.";
|
|
4900
4893
|
async function isEmptyOrAbsent(path) {
|
|
4901
4894
|
try {
|
|
4902
|
-
return (await
|
|
4895
|
+
return (await readdir5(path)).length === 0;
|
|
4903
4896
|
} catch {
|
|
4904
4897
|
return true;
|
|
4905
4898
|
}
|
|
@@ -4909,7 +4902,7 @@ async function exportRuntime(options = {}) {
|
|
|
4909
4902
|
const root = runtimeRoot(layout.roomHome);
|
|
4910
4903
|
let projects;
|
|
4911
4904
|
try {
|
|
4912
|
-
projects = (await
|
|
4905
|
+
projects = (await readdir5(join17(root, "projects"))).sort();
|
|
4913
4906
|
} catch {
|
|
4914
4907
|
projects = [];
|
|
4915
4908
|
}
|
|
@@ -4935,7 +4928,7 @@ async function exportRuntime(options = {}) {
|
|
|
4935
4928
|
}
|
|
4936
4929
|
const entry = { projectId: meta.projectId ?? name, canonicalRoot: meta.canonicalRoot ?? "unknown", events: 0, omitted: [], gateOutputs: 0 };
|
|
4937
4930
|
copies.push([join17(source, "meta.json"), join17(target, "meta.json")]);
|
|
4938
|
-
for (const file of (await
|
|
4931
|
+
for (const file of (await readdir5(join17(source, "events")).catch(() => [])).sort()) {
|
|
4939
4932
|
if (file.startsWith(".tmp-")) continue;
|
|
4940
4933
|
let valid2;
|
|
4941
4934
|
try {
|
|
@@ -4948,7 +4941,7 @@ async function exportRuntime(options = {}) {
|
|
|
4948
4941
|
copies.push([join17(source, "events", file), join17(target, "events", file)]);
|
|
4949
4942
|
} else entry.omitted.push(file);
|
|
4950
4943
|
}
|
|
4951
|
-
for (const file of (await
|
|
4944
|
+
for (const file of (await readdir5(join17(source, "gates")).catch(() => [])).sort()) {
|
|
4952
4945
|
if (file.endsWith(".result.json")) copies.push([join17(source, "gates", file), join17(target, "gates", file)]);
|
|
4953
4946
|
else if (file.endsWith(".log") && options.includeGateOutput === true) {
|
|
4954
4947
|
entry.gateOutputs += 1;
|