@akshar5/cohall 0.5.0 → 0.5.2
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/CHANGELOG.md +14 -0
- package/README.md +2 -0
- package/bin/cohall.js +67 -10
- package/bin/cohall.js.map +6 -6
- package/docs/install.md +10 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.2](https://github.com/AksharP5/cohall/compare/v0.5.1...v0.5.2) (2026-08-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **device:** support confined and Homebrew upgrades ([#54](https://github.com/AksharP5/cohall/issues/54)) ([80759fd](https://github.com/AksharP5/cohall/commit/80759fdaefa03fd8f6c7b6fd0a3f72b09c3b3d39))
|
|
9
|
+
|
|
10
|
+
## [0.5.1](https://github.com/AksharP5/cohall/compare/v0.5.0...v0.5.1) (2026-08-10)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **relay:** reject upgrades for legacy daemons ([#52](https://github.com/AksharP5/cohall/issues/52)) ([d4ab660](https://github.com/AksharP5/cohall/commit/d4ab660cce5489f6f572e221e71fc4ff6a412287))
|
|
16
|
+
|
|
3
17
|
## [0.5.0](https://github.com/AksharP5/cohall/compare/v0.4.10...v0.5.0) (2026-08-10)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -219,6 +219,8 @@ cohall upgrades abandon <operation-id>
|
|
|
219
219
|
|
|
220
220
|
Upgrade requests are durable for offline devices and run after active delegated
|
|
221
221
|
work. This is a typed owner-only maintenance operation, not remote shell access.
|
|
222
|
+
Every device must already run Cohall 0.5.0 or newer; upgrade older installations
|
|
223
|
+
individually once before using all-device upgrades.
|
|
222
224
|
Each target must use a global npm, Bun, or pnpm installation that can upgrade
|
|
223
225
|
itself. Preview the targets with `cohall upgrade --all --dry-run`, or pin an
|
|
224
226
|
exact release with `--to 1.2.3`. `cohall upgrades` returns the 50 newest results.
|
package/bin/cohall.js
CHANGED
|
@@ -17,9 +17,40 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
17
17
|
|
|
18
18
|
// packages/protocol/src/index.ts
|
|
19
19
|
import { Schema } from "effect";
|
|
20
|
-
var version = "0.5.
|
|
20
|
+
var version = "0.5.2", maxSocketPayloadBytes, bounded = (maxLength) => Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(maxLength)), optionalText = (maxLength) => Schema.String.check(Schema.isMaxLength(maxLength)), boundedArray = (schema, maxLength) => Schema.Array(schema).check(Schema.isMaxLength(maxLength)), count, uuid = (name) => Schema.String.check(Schema.isUUID(4)).pipe(Schema.brand(name)), isoTimestamp = (name) => Schema.String.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/, {
|
|
21
21
|
expected: "an ISO-8601 UTC timestamp"
|
|
22
|
-
})).pipe(Schema.brand(name)), DeviceId, ThreadId, MessageId, TaskId, AuthSessionId, OperationId, Timestamp, Platform, Provider, DeviceStatus, TaskStatus, OperationStatus, TaskTraceEventKind, MessageRole, ConnectionRole, AuthSession, CreatePairingInput, PairingCredential, ExchangePairingInput, SessionCredential, PairingResult, Capability, Workspace, Device,
|
|
22
|
+
})).pipe(Schema.brand(name)), DeviceId, ThreadId, MessageId, TaskId, AuthSessionId, OperationId, Timestamp, Platform, Provider, DeviceStatus, TaskStatus, OperationStatus, TaskTraceEventKind, MessageRole, ConnectionRole, AuthSession, CreatePairingInput, PairingCredential, ExchangePairingInput, SessionCredential, PairingResult, Capability, Workspace, Device, minimumDeviceOperationVersion = "0.5.0", parsedVersion = (value) => {
|
|
23
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
|
|
24
|
+
if (match === null) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const core = match.slice(1, 4).map(Number);
|
|
28
|
+
if (core.length !== 3 || core.some((part) => !Number.isSafeInteger(part))) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
return [core[0] ?? 0, core[1] ?? 0, core[2] ?? 0, match[4] !== undefined];
|
|
32
|
+
}, supportsDeviceOperations = (candidate) => {
|
|
33
|
+
if (candidate === "0.0.0-development") {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const current = parsedVersion(candidate);
|
|
37
|
+
const minimum = parsedVersion(minimumDeviceOperationVersion);
|
|
38
|
+
if (current === undefined || minimum === undefined) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
for (let index = 0;index < 3; index += 1) {
|
|
42
|
+
if (current[index] !== minimum[index]) {
|
|
43
|
+
return (current[index] ?? 0) > (minimum[index] ?? 0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return !current[3];
|
|
47
|
+
}, assertDeviceOperationSupport = (devices) => {
|
|
48
|
+
const unsupported = devices.filter((device) => !supportsDeviceOperations(device.version));
|
|
49
|
+
if (unsupported.length === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`All-device upgrades require Cohall ${minimumDeviceOperationVersion} or newer on every device. Upgrade individually first: ${unsupported.map((device) => `${device.name} (${device.version})`).join(", ")}`);
|
|
53
|
+
}, Thread, Message, Task, upgradeVersion, CreateUpgradeOperationsInput, DeviceOperation, TaskStatusCounts, ProviderUsage, DeviceUsage, UsageSummary, TaskTraceEvent, TaskTrace, ThreadContext, CreateTaskInput, SocketEvent, ErrorResponse, terminalTaskStatuses, isTerminalTask = (task) => terminalTaskStatuses.has(task.status), now = () => Timestamp.make(new Date().toISOString()), makeDeviceId = () => DeviceId.make(crypto.randomUUID()), makeThreadId = () => ThreadId.make(crypto.randomUUID()), makeMessageId = () => MessageId.make(crypto.randomUUID()), makeTaskId = () => TaskId.make(crypto.randomUUID()), makeAuthSessionId = () => AuthSessionId.make(crypto.randomUUID()), makeOperationId = () => OperationId.make(crypto.randomUUID()), decodeCreatePairingInput, decodeExchangePairingInput, decodeCreateTaskInput, decodeCreateUpgradeOperationsInput, decodeDevice, decodeSocketEvent;
|
|
23
54
|
var init_src = __esm(() => {
|
|
24
55
|
maxSocketPayloadBytes = 1024 * 1024;
|
|
25
56
|
count = Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER }));
|
|
@@ -820,6 +851,7 @@ var PersistenceError, Service2, operationError = (operation) => (cause) => new P
|
|
|
820
851
|
if (devices.length > 256) {
|
|
821
852
|
throw new Error("All-device operations support at most 256 registered devices");
|
|
822
853
|
}
|
|
854
|
+
assertDeviceOperationSupport(devices);
|
|
823
855
|
for (const device of devices) {
|
|
824
856
|
const outstanding = db.query(`SELECT COUNT(*) AS count FROM device_operations
|
|
825
857
|
WHERE target_device_id = ? AND status IN ('queued', 'assigned', 'running')`).get(device.id);
|
|
@@ -3378,7 +3410,7 @@ import {
|
|
|
3378
3410
|
stat,
|
|
3379
3411
|
writeFile as writeFile3
|
|
3380
3412
|
} from "node:fs/promises";
|
|
3381
|
-
import { delimiter as delimiter2, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
3413
|
+
import { delimiter as delimiter2, dirname as dirname2, extname as extname2, isAbsolute as isAbsolute2, join as join3, relative } from "node:path";
|
|
3382
3414
|
import { platform as operatingSystem } from "node:os";
|
|
3383
3415
|
var packageName = "@akshar5/cohall";
|
|
3384
3416
|
var semanticVersion = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -3448,7 +3480,19 @@ var executableNames = (command) => {
|
|
|
3448
3480
|
}
|
|
3449
3481
|
return (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((extension) => `${command}${extension.toLowerCase()}`);
|
|
3450
3482
|
};
|
|
3451
|
-
var
|
|
3483
|
+
var containsPath = (root, path) => {
|
|
3484
|
+
const child = relative(root, path);
|
|
3485
|
+
return child === "" || !child.startsWith("..") && !isAbsolute2(child);
|
|
3486
|
+
};
|
|
3487
|
+
var linuxSystemExecutableRoots = ["/bin", "/sbin", "/usr/bin", "/usr/sbin", "/nix/store"];
|
|
3488
|
+
var macosHomebrewRoots = ["/opt/homebrew", "/usr/local"];
|
|
3489
|
+
var macosAdminGroup = 80;
|
|
3490
|
+
var isTrustedSystemExecutablePath = (platform3, canonical) => platform3 === "linux" && linuxSystemExecutableRoots.some((root) => containsPath(root, canonical));
|
|
3491
|
+
var isTrustedGroupWritablePath = (options) => {
|
|
3492
|
+
const writableRoot = options.writableRoot;
|
|
3493
|
+
return options.platform === "darwin" && writableRoot !== undefined && options.uid !== undefined && options.ownerUid === options.uid && options.ownerGid === macosAdminGroup && macosHomebrewRoots.some((root) => containsPath(root, writableRoot)) && containsPath(writableRoot, options.canonical) && containsPath(writableRoot, options.path);
|
|
3494
|
+
};
|
|
3495
|
+
var trustedExecutable = async (command, options = {}) => {
|
|
3452
3496
|
const candidates = isAbsolute2(command) ? [command] : (process.env.PATH ?? "").split(delimiter2).filter(Boolean).flatMap((directory) => executableNames(command).map((name) => join3(directory, name)));
|
|
3453
3497
|
let selected;
|
|
3454
3498
|
for (const path of candidates) {
|
|
@@ -3466,12 +3510,23 @@ var trustedExecutable = async (command) => {
|
|
|
3466
3510
|
return canonical;
|
|
3467
3511
|
}
|
|
3468
3512
|
const uid = process.getuid?.();
|
|
3513
|
+
const writableRoot = options.writableRoot === undefined ? undefined : await realpath2(options.writableRoot);
|
|
3514
|
+
const systemExecutable = isTrustedSystemExecutablePath(operatingSystem(), canonical);
|
|
3469
3515
|
for (let path = canonical;; path = dirname2(path)) {
|
|
3470
3516
|
const metadata = await stat(path);
|
|
3471
|
-
|
|
3517
|
+
const trustedGroupWritablePath = isTrustedGroupWritablePath({
|
|
3518
|
+
platform: operatingSystem(),
|
|
3519
|
+
canonical,
|
|
3520
|
+
writableRoot,
|
|
3521
|
+
path,
|
|
3522
|
+
uid,
|
|
3523
|
+
ownerUid: metadata.uid,
|
|
3524
|
+
ownerGid: metadata.gid
|
|
3525
|
+
});
|
|
3526
|
+
if ((metadata.mode & 2) !== 0 || (metadata.mode & 16) !== 0 && !trustedGroupWritablePath) {
|
|
3472
3527
|
throw new Error(`Refusing executable beneath group- or world-writable path ${path}`);
|
|
3473
3528
|
}
|
|
3474
|
-
if (uid !== undefined && metadata.uid !== 0 && metadata.uid !== uid) {
|
|
3529
|
+
if (uid !== undefined && metadata.uid !== 0 && metadata.uid !== uid && !systemExecutable) {
|
|
3475
3530
|
throw new Error(`Refusing executable owned by another user at ${path}`);
|
|
3476
3531
|
}
|
|
3477
3532
|
const parent = dirname2(path);
|
|
@@ -3782,7 +3837,8 @@ var upgrade = async (options) => {
|
|
|
3782
3837
|
dry_run: true
|
|
3783
3838
|
};
|
|
3784
3839
|
}
|
|
3785
|
-
await
|
|
3840
|
+
const installExecutable = options.resolveExecutable === undefined ? await trustedExecutable(install.command, installation.prefix === undefined ? {} : { writableRoot: installation.prefix }) : await resolveExecutable(install.command);
|
|
3841
|
+
await checked(runner, { ...install, command: installExecutable });
|
|
3786
3842
|
const nextVersion = await installedVersion(entrypoint);
|
|
3787
3843
|
if (target !== "latest" && nextVersion !== target) {
|
|
3788
3844
|
throw new Error(`Installed Cohall ${nextVersion}, expected ${target}`);
|
|
@@ -4658,6 +4714,7 @@ var runCli = async (command, raw) => {
|
|
|
4658
4714
|
const restart = !arguments_.options.has("no-restart");
|
|
4659
4715
|
if (arguments_.options.has("dry-run")) {
|
|
4660
4716
|
const devices = await Effect7.runPromise(relay2.devices());
|
|
4717
|
+
assertDeviceOperationSupport(devices);
|
|
4661
4718
|
print({
|
|
4662
4719
|
dry_run: true,
|
|
4663
4720
|
requested_version: normalizedTarget,
|
|
@@ -5010,7 +5067,7 @@ import { Effect as Effect8, Schedule as Schedule2, Schema as Schema9 } from "eff
|
|
|
5010
5067
|
import { constants as constants3 } from "node:fs";
|
|
5011
5068
|
import { open, realpath as realpath5, stat as stat4 } from "node:fs/promises";
|
|
5012
5069
|
import { arch, hostname as hostname3, platform as platform3 } from "node:os";
|
|
5013
|
-
import { basename as basename2, isAbsolute as isAbsolute3, relative } from "node:path";
|
|
5070
|
+
import { basename as basename2, isAbsolute as isAbsolute3, relative as relative2 } from "node:path";
|
|
5014
5071
|
import { WebSocket as WebSocket2 } from "ws";
|
|
5015
5072
|
var maxQueuedRelayMessages = 8;
|
|
5016
5073
|
|
|
@@ -5079,7 +5136,7 @@ var allowedWorkspace = async (configuration, requested) => {
|
|
|
5079
5136
|
}
|
|
5080
5137
|
const candidate = await realpath5(selected);
|
|
5081
5138
|
const allowed = configuration.workspaces.some((root) => {
|
|
5082
|
-
const child =
|
|
5139
|
+
const child = relative2(root, candidate);
|
|
5083
5140
|
return child === "" || !child.startsWith("..") && !isAbsolute3(child);
|
|
5084
5141
|
});
|
|
5085
5142
|
if (!allowed) {
|
|
@@ -5557,4 +5614,4 @@ await main().catch((cause) => {
|
|
|
5557
5614
|
process.exitCode = 1;
|
|
5558
5615
|
});
|
|
5559
5616
|
|
|
5560
|
-
//# debugId=
|
|
5617
|
+
//# debugId=450AEED33D42A83A64756E2164756E21
|