@ours.network/cowork 1.0.6 → 1.0.7
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/README.md +2 -2
- package/dist/cli.js +2 -0
- package/dist/daemon.js +144 -16
- package/dist/web/assets/app.js +1 -1
- package/docs/03-configuration.md +2 -2
- package/docs/05-room-workflow.md +2 -2
- package/docs/08-backup-restore.md +1 -1
- package/docs/10-limitations.md +1 -1
- package/docs/11-web-console.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,9 +7,9 @@ ours-cowork web
|
|
|
7
7
|
ours-cowork docs
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
`ours-cowork web` starts the daemon if it is absent, waits for the console, and opens `http://127.0.0.1:3052/`. Create a room with a name first, then add each invitation requirement from its Invite panel. Names are trimmed, normalized to Unicode NFC, and may contain 1–64 Unicode characters excluding control and format characters.
|
|
10
|
+
`ours-cowork web` starts the daemon if it is absent, waits for the console, and opens `http://127.0.0.1:3052/`. Create a room with a name first, then add each invitation requirement from its Invite panel. Names are trimmed, normalized to Unicode NFC, and may contain 1–64 Unicode characters excluding control and format characters. The bounded identity name must not collide with an identity in the shared daemon. The Communication view contains the human-readable room chat; operational records remain in Events and the complete ordered stream remains in Archive.
|
|
11
11
|
|
|
12
|
-
Each room identity is
|
|
12
|
+
Each room identity is `ours-cowork:<bounded room name>`. Cowork NFC-normalizes the creation name and retains its first 52 Unicode code points so the complete SDK identity stays within 64 code points. The authenticated identity name is frozen at creation; later room settings may change `room_name` but do not rename the identity, CID, contacts, or history. Identity CIDs, not names, remain the authorization and routing keys. Because identity names are daemon-global, equal names—including distinct long titles with the same retained prefix—collide cleanly. Earlier unreleased ID- and slug-based identity formats are unsupported; no migration is provided for this unreleased major.
|
|
13
13
|
|
|
14
14
|
The localhost HTTP console has no authentication. Keep it bound to `127.0.0.1`; do not proxy, forward, or expose the port to other hosts. Room state is refreshed by periodic polling, not pushed to the browser.
|
|
15
15
|
|
package/dist/cli.js
CHANGED
|
@@ -4298,6 +4298,7 @@ var MAX_FILE_NAME_BYTES = 255;
|
|
|
4298
4298
|
var MAX_MIME_BYTES = 255;
|
|
4299
4299
|
var MAX_ROLE_BYTES = 256;
|
|
4300
4300
|
var MAX_ROOM_NAME_CHARACTERS = 64;
|
|
4301
|
+
var MAX_ROOM_IDENTITY_NAME_CHARACTERS = 64;
|
|
4301
4302
|
function utf8Bounded(label, maximumBytes) {
|
|
4302
4303
|
return external_exports.string().refine((value) => Buffer.byteLength(value, "utf8") >= 1, `${label} must be at least 1 UTF-8 byte`).refine(
|
|
4303
4304
|
(value) => Buffer.byteLength(value, "utf8") <= maximumBytes,
|
|
@@ -4345,6 +4346,7 @@ var RoomNameSchema = external_exports.string().refine(
|
|
|
4345
4346
|
}
|
|
4346
4347
|
});
|
|
4347
4348
|
var ROOM_IDENTITY_PREFIX = "ours-cowork:";
|
|
4349
|
+
var MAX_ROOM_IDENTITY_TITLE_CHARACTERS = MAX_ROOM_IDENTITY_NAME_CHARACTERS - Array.from(ROOM_IDENTITY_PREFIX).length;
|
|
4348
4350
|
function isPersistedRoomIdentityName(roomId, identityName) {
|
|
4349
4351
|
if (!LowerCrockfordUlidSchema.safeParse(roomId).success || !identityName.startsWith(ROOM_IDENTITY_PREFIX)) {
|
|
4350
4352
|
return false;
|
package/dist/daemon.js
CHANGED
|
@@ -4540,8 +4540,27 @@ function isStrictRfc3339(value) {
|
|
|
4540
4540
|
function normalizeRoomName(value) {
|
|
4541
4541
|
return value.trim().normalize("NFC");
|
|
4542
4542
|
}
|
|
4543
|
+
function sdkIdentityNameError(identityName) {
|
|
4544
|
+
const length = Array.from(identityName).length;
|
|
4545
|
+
if (length < 1 || length > MAX_ROOM_IDENTITY_NAME_CHARACTERS) {
|
|
4546
|
+
return `name must be 1-${MAX_ROOM_IDENTITY_NAME_CHARACTERS} Unicode characters`;
|
|
4547
|
+
}
|
|
4548
|
+
if (identityName !== identityName.normalize("NFC")) return "name must use Unicode NFC normalization";
|
|
4549
|
+
if (SDK_IDENTITY_NAME_FORBIDDEN.test(identityName)) {
|
|
4550
|
+
return "name must not contain control, format, surrogate, line-separator, or path-separator characters";
|
|
4551
|
+
}
|
|
4552
|
+
if (SDK_IDENTITY_NAME_RESERVED.has(identityName)) return `name ${JSON.stringify(identityName)} is reserved`;
|
|
4553
|
+
return void 0;
|
|
4554
|
+
}
|
|
4543
4555
|
function roomIdentityName(roomName) {
|
|
4544
|
-
|
|
4556
|
+
const normalized = RoomNameSchema.parse(roomName);
|
|
4557
|
+
const boundedTitle = Array.from(normalized).slice(0, MAX_ROOM_IDENTITY_TITLE_CHARACTERS).join("");
|
|
4558
|
+
const identityName = `${ROOM_IDENTITY_PREFIX}${boundedTitle}`;
|
|
4559
|
+
const detail = sdkIdentityNameError(identityName);
|
|
4560
|
+
if (detail !== void 0) {
|
|
4561
|
+
throw new CoworkIdentityNameError(`generated room identity name is invalid: ${detail}`);
|
|
4562
|
+
}
|
|
4563
|
+
return identityName;
|
|
4545
4564
|
}
|
|
4546
4565
|
function isPersistedRoomIdentityName(roomId, identityName) {
|
|
4547
4566
|
if (!LowerCrockfordUlidSchema.safeParse(roomId).success || !identityName.startsWith(ROOM_IDENTITY_PREFIX)) {
|
|
@@ -4551,7 +4570,7 @@ function isPersistedRoomIdentityName(roomId, identityName) {
|
|
|
4551
4570
|
return parsed.success && identityName === `${ROOM_IDENTITY_PREFIX}${parsed.data}`;
|
|
4552
4571
|
}
|
|
4553
4572
|
function isStandardRoomIdentityName(roomId, identityName) {
|
|
4554
|
-
return isPersistedRoomIdentityName(roomId, identityName);
|
|
4573
|
+
return isPersistedRoomIdentityName(roomId, identityName) && sdkIdentityNameError(identityName) === void 0;
|
|
4555
4574
|
}
|
|
4556
4575
|
function refineRoomLineage(room, context) {
|
|
4557
4576
|
const exactPacketPending = room.state === "provisioning" && room.status === "packet_pending" && isPersistedRoomIdentityName(room.room_id, room.identity_name) && room.invites.length === 0 && room.seats.length === 0 && room.activated_at === void 0 && room.closed_at === void 0;
|
|
@@ -4681,7 +4700,7 @@ function refineMessageCategory(message, context) {
|
|
|
4681
4700
|
}
|
|
4682
4701
|
}
|
|
4683
4702
|
}
|
|
4684
|
-
var MAX_TEXT_BYTES, MAX_FILE_BYTES, MAX_HISTORY_PAGE_BYTES, MAX_MANAGEMENT_RESPONSE_BYTES, MAX_EXTERNAL_INVITE_BYTES, MAX_FILE_NAME_BYTES, MAX_MIME_BYTES, MAX_ROLE_BYTES, MAX_ROOM_NAME_CHARACTERS, NonEmptyStringSchema, PositiveSafeIntegerSchema, LowerCrockfordUlidSchema, Rfc3339Schema, RoomNameSchema, ROOM_IDENTITY_PREFIX, RoleSchema, ROOM_ROLE, MissionTextSchema, MessageTextSchema, FileNameSchema, FileMimeSchema, RoomStateSchema, SeatStateSchema, InviteModeSchema, DEFAULT_ROLE, InviteStateSchema, RelayStatusSchema, SeatV1Schema, SeatSchema, RoomInviteSchema, MissionV1Schema, MissionSchema, RoleBriefingSchema, RoomCommonShape, RoomV1Schema, CurrentRoomSchema, RoomSchema, CreateRoomInputSchema, UpdateRoomInputSchema, RoleBriefingSetInputSchema, RoleBriefingDeleteInputSchema, PostMessageInputSchema, PostAsRoleInputSchema, RestRoleInputSchema, ContainerIdSchema, AcceptExternalInviteInputSchema, AuthorSnapshotSchema, RecordCommonShape, AppendCommonShape, MembershipNoticeSchema, AuthorAliasSchema, ReplyReferenceSchema, MessageShape, RelayIntentShape, RelayResultStatusSchema, RelayResultShape, FileShape, MembershipIntentShape, MembershipResultShape, CloseNoticeIntentShape, CloseNoticeResultShape, MessageRecordSchema, FileRecordSchema, RelayIntentRecordSchema, RelayResultRecordSchema, MembershipIntentRecordSchema, MembershipResultRecordSchema, CloseNoticeIntentRecordSchema, CloseNoticeResultRecordSchema, RawCommunicationRecordSchema, CommunicationRecordSchema, AppendRecordSchema;
|
|
4703
|
+
var MAX_TEXT_BYTES, MAX_FILE_BYTES, MAX_HISTORY_PAGE_BYTES, MAX_MANAGEMENT_RESPONSE_BYTES, MAX_EXTERNAL_INVITE_BYTES, MAX_FILE_NAME_BYTES, MAX_MIME_BYTES, MAX_ROLE_BYTES, MAX_ROOM_NAME_CHARACTERS, MAX_ROOM_IDENTITY_NAME_CHARACTERS, NonEmptyStringSchema, PositiveSafeIntegerSchema, LowerCrockfordUlidSchema, Rfc3339Schema, RoomNameSchema, ROOM_IDENTITY_PREFIX, MAX_ROOM_IDENTITY_TITLE_CHARACTERS, SDK_IDENTITY_NAME_FORBIDDEN, SDK_IDENTITY_NAME_RESERVED, CoworkIdentityNameError, RoleSchema, ROOM_ROLE, MissionTextSchema, MessageTextSchema, FileNameSchema, FileMimeSchema, RoomStateSchema, SeatStateSchema, InviteModeSchema, DEFAULT_ROLE, InviteStateSchema, RelayStatusSchema, SeatV1Schema, SeatSchema, RoomInviteSchema, MissionV1Schema, MissionSchema, RoleBriefingSchema, RoomCommonShape, RoomV1Schema, CurrentRoomSchema, RoomSchema, CreateRoomInputSchema, UpdateRoomInputSchema, RoleBriefingSetInputSchema, RoleBriefingDeleteInputSchema, PostMessageInputSchema, PostAsRoleInputSchema, RestRoleInputSchema, ContainerIdSchema, AcceptExternalInviteInputSchema, AuthorSnapshotSchema, RecordCommonShape, AppendCommonShape, MembershipNoticeSchema, AuthorAliasSchema, ReplyReferenceSchema, MessageShape, RelayIntentShape, RelayResultStatusSchema, RelayResultShape, FileShape, MembershipIntentShape, MembershipResultShape, CloseNoticeIntentShape, CloseNoticeResultShape, MessageRecordSchema, FileRecordSchema, RelayIntentRecordSchema, RelayResultRecordSchema, MembershipIntentRecordSchema, MembershipResultRecordSchema, CloseNoticeIntentRecordSchema, CloseNoticeResultRecordSchema, RawCommunicationRecordSchema, CommunicationRecordSchema, AppendRecordSchema;
|
|
4685
4704
|
var init_contracts = __esm({
|
|
4686
4705
|
"src/contracts.ts"() {
|
|
4687
4706
|
"use strict";
|
|
@@ -4695,6 +4714,7 @@ var init_contracts = __esm({
|
|
|
4695
4714
|
MAX_MIME_BYTES = 255;
|
|
4696
4715
|
MAX_ROLE_BYTES = 256;
|
|
4697
4716
|
MAX_ROOM_NAME_CHARACTERS = 64;
|
|
4717
|
+
MAX_ROOM_IDENTITY_NAME_CHARACTERS = 64;
|
|
4698
4718
|
NonEmptyStringSchema = external_exports.string().min(1);
|
|
4699
4719
|
PositiveSafeIntegerSchema = external_exports.number().int().positive().safe();
|
|
4700
4720
|
LowerCrockfordUlidSchema = external_exports.string().regex(
|
|
@@ -4715,6 +4735,16 @@ var init_contracts = __esm({
|
|
|
4715
4735
|
}
|
|
4716
4736
|
});
|
|
4717
4737
|
ROOM_IDENTITY_PREFIX = "ours-cowork:";
|
|
4738
|
+
MAX_ROOM_IDENTITY_TITLE_CHARACTERS = MAX_ROOM_IDENTITY_NAME_CHARACTERS - Array.from(ROOM_IDENTITY_PREFIX).length;
|
|
4739
|
+
SDK_IDENTITY_NAME_FORBIDDEN = /[\\/\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u;
|
|
4740
|
+
SDK_IDENTITY_NAME_RESERVED = /* @__PURE__ */ new Set([".", "..", "contact-book", "root.json", "bindings.json"]);
|
|
4741
|
+
CoworkIdentityNameError = class extends Error {
|
|
4742
|
+
code = "NAME_INVALID";
|
|
4743
|
+
constructor(message) {
|
|
4744
|
+
super(message);
|
|
4745
|
+
this.name = "CoworkIdentityNameError";
|
|
4746
|
+
}
|
|
4747
|
+
};
|
|
4718
4748
|
RoleSchema = utf8Bounded("role", MAX_ROLE_BYTES);
|
|
4719
4749
|
ROOM_ROLE = "room";
|
|
4720
4750
|
MissionTextSchema = utf8Bounded("mission text", MAX_TEXT_BYTES);
|
|
@@ -5267,6 +5297,16 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
5267
5297
|
import * as fs from "node:fs";
|
|
5268
5298
|
import { join as join2 } from "node:path";
|
|
5269
5299
|
import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from "node:zlib";
|
|
5300
|
+
async function sdkNameError(code, message) {
|
|
5301
|
+
const { OursError } = await import("@ours.network/sdk");
|
|
5302
|
+
return new OursError(code, message);
|
|
5303
|
+
}
|
|
5304
|
+
async function isTypedNameRefusal(error) {
|
|
5305
|
+
const { OursError } = await import("@ours.network/sdk");
|
|
5306
|
+
if (!(error instanceof OursError)) return false;
|
|
5307
|
+
const code = error.code;
|
|
5308
|
+
return code === "NAME_INVALID" || code === "NAME_TAKEN";
|
|
5309
|
+
}
|
|
5270
5310
|
function sdkErrorCode(error) {
|
|
5271
5311
|
if (error === null || typeof error !== "object" || !("code" in error)) return void 0;
|
|
5272
5312
|
return typeof error.code === "string" ? error.code : void 0;
|
|
@@ -5450,18 +5490,22 @@ var init_packets = __esm({
|
|
|
5450
5490
|
get(roomId) {
|
|
5451
5491
|
return this.packets.get(roomId);
|
|
5452
5492
|
}
|
|
5453
|
-
async
|
|
5493
|
+
async preflightCreate(roomId, identityName) {
|
|
5454
5494
|
validateRoomId(roomId);
|
|
5455
|
-
this.assertStandardIdentity(roomId, identityName);
|
|
5495
|
+
await this.assertStandardIdentity(roomId, identityName);
|
|
5456
5496
|
this.assertNoLegacyState(roomId);
|
|
5457
5497
|
if (this.packets.has(roomId)) throw new Error(`room identity "${roomId}" is already hosted`);
|
|
5458
5498
|
const localNames = /* @__PURE__ */ new Set([identityName]);
|
|
5459
5499
|
const available = await this.host.listIdentityNames(localNames);
|
|
5460
5500
|
if (available.has(identityName)) {
|
|
5461
|
-
throw
|
|
5501
|
+
throw await sdkNameError(
|
|
5502
|
+
"NAME_TAKEN",
|
|
5462
5503
|
`shared ours daemon already contains unproven room identity "${identityName}"; refusing to adopt it without a durably recorded CID`
|
|
5463
5504
|
);
|
|
5464
5505
|
}
|
|
5506
|
+
}
|
|
5507
|
+
async create(roomId, identityName, bio = `ours-cowork mission room ${roomId}`) {
|
|
5508
|
+
await this.preflightCreate(roomId, identityName);
|
|
5465
5509
|
const client = await this.host.createClient();
|
|
5466
5510
|
try {
|
|
5467
5511
|
const created = await client.createIdentity({
|
|
@@ -5484,12 +5528,15 @@ var init_packets = __esm({
|
|
|
5484
5528
|
} catch (error) {
|
|
5485
5529
|
await client.releaseLease().catch(() => {
|
|
5486
5530
|
});
|
|
5531
|
+
if (await isTypedNameRefusal(error)) {
|
|
5532
|
+
throw error;
|
|
5533
|
+
}
|
|
5487
5534
|
throw new Error(`failed to provision standard SDK identity for room "${roomId}"`, { cause: error });
|
|
5488
5535
|
}
|
|
5489
5536
|
}
|
|
5490
5537
|
async restore(roomId, expectedCid, identityName) {
|
|
5491
5538
|
validateRoomId(roomId);
|
|
5492
|
-
this.assertStandardIdentity(roomId, identityName);
|
|
5539
|
+
await this.assertStandardIdentity(roomId, identityName);
|
|
5493
5540
|
this.assertNoLegacyState(roomId);
|
|
5494
5541
|
if (expectedCid === void 0 || expectedCid.length === 0) {
|
|
5495
5542
|
throw new Error(`refusing to restore room identity "${identityName}" without a durably recorded expected CID`);
|
|
@@ -5584,7 +5631,9 @@ var init_packets = __esm({
|
|
|
5584
5631
|
throw new LegacyCoworkStateError(roomId);
|
|
5585
5632
|
}
|
|
5586
5633
|
}
|
|
5587
|
-
assertStandardIdentity(roomId, identityName) {
|
|
5634
|
+
async assertStandardIdentity(roomId, identityName) {
|
|
5635
|
+
const detail = sdkIdentityNameError(identityName);
|
|
5636
|
+
if (detail !== void 0) throw await sdkNameError("NAME_INVALID", `generated room identity name is invalid: ${detail}`);
|
|
5588
5637
|
if (!isStandardRoomIdentityName(roomId, identityName)) throw new LegacyCoworkStateError(roomId);
|
|
5589
5638
|
}
|
|
5590
5639
|
};
|
|
@@ -6345,6 +6394,12 @@ function byteBoundedHistoryPage(records) {
|
|
|
6345
6394
|
}
|
|
6346
6395
|
return page;
|
|
6347
6396
|
}
|
|
6397
|
+
async function isCleanIdentityRefusal(error) {
|
|
6398
|
+
const { OursError } = await import("@ours.network/sdk");
|
|
6399
|
+
if (!(error instanceof OursError)) return false;
|
|
6400
|
+
const code = error.code;
|
|
6401
|
+
return code === "NAME_INVALID" || code === "NAME_TAKEN";
|
|
6402
|
+
}
|
|
6348
6403
|
function activeSeats(room) {
|
|
6349
6404
|
return room.seats.filter((seat) => seat.state === "active");
|
|
6350
6405
|
}
|
|
@@ -6430,6 +6485,7 @@ var init_service = __esm({
|
|
|
6430
6485
|
nextMessageId;
|
|
6431
6486
|
intake;
|
|
6432
6487
|
provisioningCheckpoint;
|
|
6488
|
+
identityNameTails = /* @__PURE__ */ new Map();
|
|
6433
6489
|
constructor(store, packets, options = {}) {
|
|
6434
6490
|
this.store = store;
|
|
6435
6491
|
this.packets = packets;
|
|
@@ -6448,7 +6504,8 @@ var init_service = __esm({
|
|
|
6448
6504
|
const roomId = LowerCrockfordUlidSchema.parse(this.nextRoomId());
|
|
6449
6505
|
const roomName = settings.name ?? defaultRoomName(roomId);
|
|
6450
6506
|
const identityName = roomIdentityName(roomName);
|
|
6451
|
-
return this.lock(roomId, async () => {
|
|
6507
|
+
return this.lockIdentityName(identityName, () => this.lock(roomId, async () => {
|
|
6508
|
+
await this.packets.preflightCreate(roomId, identityName);
|
|
6452
6509
|
const provisional = RoomSchema.parse({
|
|
6453
6510
|
version: 2,
|
|
6454
6511
|
room_id: roomId,
|
|
@@ -6471,15 +6528,30 @@ var init_service = __esm({
|
|
|
6471
6528
|
created_at: this.now()
|
|
6472
6529
|
});
|
|
6473
6530
|
await this.store.create(provisional);
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6531
|
+
let packet;
|
|
6532
|
+
try {
|
|
6533
|
+
packet = await this.packets.create(
|
|
6534
|
+
roomId,
|
|
6535
|
+
identityName,
|
|
6536
|
+
`ours-cowork mission room ${roomId}`
|
|
6537
|
+
);
|
|
6538
|
+
} catch (error) {
|
|
6539
|
+
if (await isCleanIdentityRefusal(error)) {
|
|
6540
|
+
try {
|
|
6541
|
+
await this.store.discardPendingProvisioning(roomId, identityName);
|
|
6542
|
+
} catch (rollbackError) {
|
|
6543
|
+
throw new AggregateError(
|
|
6544
|
+
[error, rollbackError],
|
|
6545
|
+
`room "${roomId}" identity provisioning was refused and its fresh sentinel could not be discarded`
|
|
6546
|
+
);
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
throw error;
|
|
6550
|
+
}
|
|
6479
6551
|
this.provisioningCheckpoint("metadata");
|
|
6480
6552
|
const { status: _packetPending, ...created } = provisional;
|
|
6481
6553
|
return this.store.save(RoomSchema.parse({ ...created, identity_cid: packet.cid }));
|
|
6482
|
-
});
|
|
6554
|
+
}));
|
|
6483
6555
|
}
|
|
6484
6556
|
async recoverRoom(roomId) {
|
|
6485
6557
|
const id = LowerCrockfordUlidSchema.parse(roomId);
|
|
@@ -6520,7 +6592,13 @@ var init_service = __esm({
|
|
|
6520
6592
|
async recoverPacketUnlocked(id, initial) {
|
|
6521
6593
|
let room = initial;
|
|
6522
6594
|
if (room.state === "closed") return room;
|
|
6523
|
-
|
|
6595
|
+
let packetPending = this.isPacketPending(room);
|
|
6596
|
+
if (packetPending && isPersistedRoomIdentityName(id, room.identity_name) && !isStandardRoomIdentityName(id, room.identity_name)) {
|
|
6597
|
+
const storedCreationName = room.identity_name.slice(ROOM_IDENTITY_PREFIX.length);
|
|
6598
|
+
const correctedIdentityName = roomIdentityName(storedCreationName);
|
|
6599
|
+
room = await this.store.save(RoomSchema.parse({ ...room, identity_name: correctedIdentityName }));
|
|
6600
|
+
packetPending = this.isPacketPending(room);
|
|
6601
|
+
}
|
|
6524
6602
|
let packet = this.packets.get(id);
|
|
6525
6603
|
if (packet) {
|
|
6526
6604
|
if (!packetPending && packet.cid !== room.identity_cid) {
|
|
@@ -7679,6 +7757,17 @@ var init_service = __esm({
|
|
|
7679
7757
|
lock(roomId, work) {
|
|
7680
7758
|
return this.store.mutex(roomId).runExclusive(work);
|
|
7681
7759
|
}
|
|
7760
|
+
async lockIdentityName(identityName, work) {
|
|
7761
|
+
const previous = this.identityNameTails.get(identityName) ?? Promise.resolve();
|
|
7762
|
+
const result = previous.then(work, work);
|
|
7763
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
7764
|
+
this.identityNameTails.set(identityName, tail);
|
|
7765
|
+
try {
|
|
7766
|
+
return await result;
|
|
7767
|
+
} finally {
|
|
7768
|
+
if (this.identityNameTails.get(identityName) === tail) this.identityNameTails.delete(identityName);
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
7682
7771
|
packet(roomId) {
|
|
7683
7772
|
const packet = this.packets.get(roomId);
|
|
7684
7773
|
if (!packet) throw new RoomServiceError(`room packet "${roomId}" is not hosted`);
|
|
@@ -8025,6 +8114,45 @@ var init_storage = __esm({
|
|
|
8025
8114
|
synchronous: db.pragma("synchronous", { simple: true })
|
|
8026
8115
|
})));
|
|
8027
8116
|
}
|
|
8117
|
+
/** Roll back only a fresh, empty provisioning sentinel after a proven no-side-effect SDK refusal. */
|
|
8118
|
+
async discardPendingProvisioning(roomId, expectedIdentityName) {
|
|
8119
|
+
const id = this.roomId(roomId);
|
|
8120
|
+
await this.mutex(id, () => {
|
|
8121
|
+
this.ensureBaseDirectories();
|
|
8122
|
+
const roomDir = this.roomDirectory(id);
|
|
8123
|
+
this.ensurePrivateDirectory(roomDir, false, `room "${id}" directory`);
|
|
8124
|
+
const room = this.loadUnlocked(id);
|
|
8125
|
+
if (room.state !== "provisioning" || room.status !== "packet_pending" || room.identity_cid !== "" || room.identity_name !== expectedIdentityName || room.invites.length !== 0 || room.seats.length !== 0) {
|
|
8126
|
+
throw new CoworkStorageError(`room "${id}" is not the exact empty provisioning sentinel`);
|
|
8127
|
+
}
|
|
8128
|
+
const blobs = this.blobsDirectory(id);
|
|
8129
|
+
this.ensurePrivateDirectory(blobs, false, `room "${id}" blobs directory`);
|
|
8130
|
+
if (this.fs.readdirSync(blobs).length !== 0) {
|
|
8131
|
+
throw new CoworkStorageError(`room "${id}" provisioning sentinel has file blobs`);
|
|
8132
|
+
}
|
|
8133
|
+
const recordCount = this.withDatabase(id, (db) => db.prepare(
|
|
8134
|
+
"SELECT COUNT(*) AS count FROM records"
|
|
8135
|
+
).get().count);
|
|
8136
|
+
if (recordCount !== 0) throw new CoworkStorageError(`room "${id}" provisioning sentinel has archive records`);
|
|
8137
|
+
const expected = /* @__PURE__ */ new Set(["archive.sqlite3", "archive.sqlite3-wal", "archive.sqlite3-shm", "blobs", "room.json"]);
|
|
8138
|
+
const unexpected = this.fs.readdirSync(roomDir).filter((name) => !expected.has(name));
|
|
8139
|
+
if (unexpected.length !== 0) {
|
|
8140
|
+
throw new CoworkStorageError(`room "${id}" provisioning sentinel has unexpected residue: ${unexpected.join(", ")}`);
|
|
8141
|
+
}
|
|
8142
|
+
for (const name of ["archive.sqlite3-wal", "archive.sqlite3-shm", "archive.sqlite3", "room.json"]) {
|
|
8143
|
+
const path = join3(roomDir, name);
|
|
8144
|
+
if (this.lstatIfPresent(path)) {
|
|
8145
|
+
this.assertRegularFile(path, name);
|
|
8146
|
+
this.fs.unlinkSync(path);
|
|
8147
|
+
}
|
|
8148
|
+
}
|
|
8149
|
+
this.fs.rmdirSync(blobs);
|
|
8150
|
+
this.fsyncDirectory(roomDir);
|
|
8151
|
+
this.fs.rmdirSync(roomDir);
|
|
8152
|
+
this.fsyncDirectory(this.roomsDirectory());
|
|
8153
|
+
this.reconciledBlobRooms.delete(id);
|
|
8154
|
+
});
|
|
8155
|
+
}
|
|
8028
8156
|
async delete(roomId) {
|
|
8029
8157
|
const id = this.roomId(roomId);
|
|
8030
8158
|
await this.mutex(id, () => {
|