@akshar5/cohall 0.3.3 → 0.4.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/CHANGELOG.md +20 -0
- package/README.md +5 -0
- package/bin/cohall.js +577 -186
- package/bin/cohall.js.map +7 -6
- package/docs/install.md +16 -4
- package/docs/services.md +12 -0
- package/package.json +1 -1
package/bin/cohall.js
CHANGED
|
@@ -17,7 +17,7 @@ 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.
|
|
20
|
+
var version = "0.4.0", 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)), 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
22
|
})).pipe(Schema.brand(name)), DeviceId, ThreadId, MessageId, TaskId, AuthSessionId, Timestamp, Platform, Provider, DeviceStatus, TaskStatus, TaskTraceEventKind, MessageRole, ConnectionRole, AuthSession, CreatePairingInput, PairingCredential, ExchangePairingInput, SessionCredential, PairingResult, Capability, Workspace, Device, Thread, Message, Task, 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()), decodeCreatePairingInput, decodeExchangePairingInput, decodeCreateTaskInput, decodeDevice, decodeSocketEvent;
|
|
23
23
|
var init_src = __esm(() => {
|
|
@@ -201,27 +201,27 @@ var init_src = __esm(() => {
|
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
// apps/relay/src/config.ts
|
|
204
|
-
import { Effect as Effect8, Schema as
|
|
205
|
-
import { access as access3, chmod as
|
|
204
|
+
import { Effect as Effect8, Schema as Schema9 } from "effect";
|
|
205
|
+
import { access as access3, chmod as chmod4, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile5 } from "node:fs/promises";
|
|
206
206
|
import { homedir as homedir4, platform as platform4 } from "node:os";
|
|
207
|
-
import { join as
|
|
207
|
+
import { join as join5, resolve as resolve2 } from "node:path";
|
|
208
208
|
var RelayConfiguration, defaultDataDirectory = () => {
|
|
209
209
|
if (platform4() === "win32") {
|
|
210
|
-
return
|
|
210
|
+
return join5(process.env.LOCALAPPDATA ?? join5(homedir4(), "AppData", "Local"), "Cohall");
|
|
211
211
|
}
|
|
212
212
|
if (platform4() === "darwin") {
|
|
213
|
-
return
|
|
213
|
+
return join5(homedir4(), "Library", "Application Support", "Cohall", "relay");
|
|
214
214
|
}
|
|
215
|
-
return
|
|
215
|
+
return join5(process.env.XDG_DATA_HOME ?? join5(homedir4(), ".local", "share"), "cohall");
|
|
216
216
|
}, generatedToken = () => Array.from(crypto.getRandomValues(new Uint8Array(32)), (value) => value.toString(16).padStart(2, "0")).join(""), tokenFrom = async (path) => {
|
|
217
217
|
if (await access3(path).then(() => true).catch(() => false)) {
|
|
218
|
-
return (await
|
|
218
|
+
return (await readFile4(path, "utf8")).trim();
|
|
219
219
|
}
|
|
220
220
|
const token = generatedToken();
|
|
221
|
-
await
|
|
221
|
+
await writeFile5(path, `${token}
|
|
222
222
|
`, { mode: 384 });
|
|
223
223
|
if (platform4() !== "win32") {
|
|
224
|
-
await
|
|
224
|
+
await chmod4(path, 384);
|
|
225
225
|
}
|
|
226
226
|
return token;
|
|
227
227
|
}, booleanEnvironment = (name) => {
|
|
@@ -235,13 +235,13 @@ var RelayConfiguration, defaultDataDirectory = () => {
|
|
|
235
235
|
throw new Error(`${name} must be true or false`);
|
|
236
236
|
}, isLoopback = (host) => host === "127.0.0.1" || host === "localhost" || host === "::1", loadEnvironmentConfiguration;
|
|
237
237
|
var init_config = __esm(() => {
|
|
238
|
-
RelayConfiguration =
|
|
239
|
-
host:
|
|
240
|
-
port:
|
|
241
|
-
token:
|
|
242
|
-
dataDirectory:
|
|
243
|
-
databasePath:
|
|
244
|
-
allowRemote:
|
|
238
|
+
RelayConfiguration = Schema9.Struct({
|
|
239
|
+
host: Schema9.NonEmptyString,
|
|
240
|
+
port: Schema9.Int.check(Schema9.isBetween({ minimum: 1, maximum: 65535 })),
|
|
241
|
+
token: Schema9.NonEmptyString,
|
|
242
|
+
dataDirectory: Schema9.NonEmptyString,
|
|
243
|
+
databasePath: Schema9.NonEmptyString,
|
|
244
|
+
allowRemote: Schema9.Boolean
|
|
245
245
|
});
|
|
246
246
|
loadEnvironmentConfiguration = Effect8.tryPromise({
|
|
247
247
|
try: async () => {
|
|
@@ -252,17 +252,17 @@ var init_config = __esm(() => {
|
|
|
252
252
|
throw new Error("Remote relay binding is disabled. Use a private network or TLS reverse proxy, then set COHALL_RELAY_ALLOW_REMOTE=true.");
|
|
253
253
|
}
|
|
254
254
|
const dataDirectory = resolve2(process.env.COHALL_DATA_DIR ?? defaultDataDirectory());
|
|
255
|
-
await
|
|
255
|
+
await mkdir4(dataDirectory, { recursive: true, mode: 448 });
|
|
256
256
|
if (platform4() !== "win32") {
|
|
257
|
-
await
|
|
257
|
+
await chmod4(dataDirectory, 448);
|
|
258
258
|
}
|
|
259
|
-
const token = process.env.COHALL_TOKEN ?? await tokenFrom(
|
|
260
|
-
return
|
|
259
|
+
const token = process.env.COHALL_TOKEN ?? await tokenFrom(join5(dataDirectory, "owner-token"));
|
|
260
|
+
return Schema9.decodeUnknownSync(RelayConfiguration)({
|
|
261
261
|
host,
|
|
262
262
|
port,
|
|
263
263
|
token,
|
|
264
264
|
dataDirectory,
|
|
265
|
-
databasePath:
|
|
265
|
+
databasePath: join5(dataDirectory, "cohall.db"),
|
|
266
266
|
allowRemote
|
|
267
267
|
});
|
|
268
268
|
},
|
|
@@ -408,12 +408,12 @@ __export(exports_store, {
|
|
|
408
408
|
RelayStore: () => exports_store,
|
|
409
409
|
PersistenceError: () => PersistenceError
|
|
410
410
|
});
|
|
411
|
-
import { Context as Context2, Effect as Effect9, Layer as Layer2, Schema as
|
|
411
|
+
import { Context as Context2, Effect as Effect9, Layer as Layer2, Schema as Schema10 } from "effect";
|
|
412
412
|
import { createHash, randomBytes } from "node:crypto";
|
|
413
413
|
var PersistenceError, Service2, operationError = (operation) => (cause) => new PersistenceError({
|
|
414
414
|
operation,
|
|
415
415
|
message: cause instanceof Error ? cause.message : String(cause)
|
|
416
|
-
}), decode = (operation, schema, value) =>
|
|
416
|
+
}), decode = (operation, schema, value) => Schema10.decodeUnknownEffect(schema)(value).pipe(Effect9.mapError(operationError(operation))), threadFromRow = (row) => decode("RelayStore.decodeThread", Thread, {
|
|
417
417
|
id: row.id,
|
|
418
418
|
title: row.title,
|
|
419
419
|
createdAt: row.created_at,
|
|
@@ -463,7 +463,7 @@ var PersistenceError, Service2, operationError = (operation) => (cause) => new P
|
|
|
463
463
|
kind: row.kind,
|
|
464
464
|
at: row.created_at,
|
|
465
465
|
...row.detail === null ? {} : { detail: row.detail }
|
|
466
|
-
}), rolesFromRow = (row) =>
|
|
466
|
+
}), rolesFromRow = (row) => Schema10.decodeUnknownSync(Schema10.Array(ConnectionRole))(JSON.parse(row.roles_json)), authSessionFromRow = (row) => {
|
|
467
467
|
const roles = rolesFromRow(row);
|
|
468
468
|
const role = roles[0];
|
|
469
469
|
return role === undefined ? Effect9.fail(new PersistenceError({
|
|
@@ -904,7 +904,7 @@ var PersistenceError, Service2, operationError = (operation) => (cause) => new P
|
|
|
904
904
|
if (pairing === null) {
|
|
905
905
|
throw new Error("Pairing credential is invalid, expired, or already used");
|
|
906
906
|
}
|
|
907
|
-
const roles =
|
|
907
|
+
const roles = Schema10.decodeUnknownSync(Schema10.Array(ConnectionRole))(JSON.parse(pairing.roles_json));
|
|
908
908
|
const deviceId = roles.includes("device") ? makeDeviceId() : undefined;
|
|
909
909
|
const consumed = db.query("UPDATE auth_pairings SET used_at = ? WHERE token_hash = ? AND used_at IS NULL").run(timestamp, pairing.token_hash);
|
|
910
910
|
if (consumed.changes !== 1) {
|
|
@@ -1075,7 +1075,7 @@ var init_store = __esm(() => {
|
|
|
1075
1075
|
init_src();
|
|
1076
1076
|
init_database();
|
|
1077
1077
|
init_store();
|
|
1078
|
-
PersistenceError = class PersistenceError extends
|
|
1078
|
+
PersistenceError = class PersistenceError extends Schema10.TaggedErrorClass()("RelayStore.PersistenceError", { operation: Schema10.String, message: Schema10.String }) {
|
|
1079
1079
|
};
|
|
1080
1080
|
Service2 = class Service2 extends Context2.Service()("@cohall/RelayStore") {
|
|
1081
1081
|
};
|
|
@@ -1086,9 +1086,9 @@ var exports_main = {};
|
|
|
1086
1086
|
__export(exports_main, {
|
|
1087
1087
|
runRelay: () => runRelay
|
|
1088
1088
|
});
|
|
1089
|
-
import { Effect as Effect10, ManagedRuntime, Schema as
|
|
1089
|
+
import { Effect as Effect10, ManagedRuntime, Schema as Schema11 } from "effect";
|
|
1090
1090
|
import { createHash as createHash2, timingSafeEqual } from "node:crypto";
|
|
1091
|
-
import { access as access4, chmod as
|
|
1091
|
+
import { access as access4, chmod as chmod5 } from "node:fs/promises";
|
|
1092
1092
|
import { createServer } from "node:http";
|
|
1093
1093
|
import { platform as platform5 } from "node:os";
|
|
1094
1094
|
import { WebSocketServer } from "ws";
|
|
@@ -1173,7 +1173,7 @@ Content-Length: 0\r
|
|
|
1173
1173
|
}, body = (request, decode2) => Effect10.tryPromise({
|
|
1174
1174
|
try: () => request.json(),
|
|
1175
1175
|
catch: () => new RequestError({ status: 400, message: "Expected a JSON request body" })
|
|
1176
|
-
}).pipe(Effect10.flatMap(decode2), Effect10.mapError((cause) => cause instanceof RequestError ? cause : new RequestError({ status: 400, message: String(cause).slice(0, 2048) }))), pathId = (schema, value) =>
|
|
1176
|
+
}).pipe(Effect10.flatMap(decode2), Effect10.mapError((cause) => cause instanceof RequestError ? cause : new RequestError({ status: 400, message: String(cause).slice(0, 2048) }))), pathId = (schema, value) => Schema11.decodeUnknownEffect(schema)(value).pipe(Effect10.mapError(() => new RequestError({ status: 400, message: `Invalid identifier: ${value}` }))), scoreDevice = (device, input, sourceDeviceId) => {
|
|
1177
1177
|
const prompt = `${input.prompt} ${input.context ?? ""}`.toLowerCase();
|
|
1178
1178
|
const needsXcode = /\b(xcode|ios|macos|swiftui|app store)\b/.test(prompt);
|
|
1179
1179
|
const needsBrowser = /\b(browser|chrome|youtube|twitter|logged[- ]?in|signed[- ]?in|x\.com)\b/.test(prompt);
|
|
@@ -1216,7 +1216,7 @@ Content-Length: 0\r
|
|
|
1216
1216
|
yield* store.recover();
|
|
1217
1217
|
}));
|
|
1218
1218
|
if (platform5() !== "win32" && await access4(configuration.databasePath).then(() => true).catch(() => false)) {
|
|
1219
|
-
await
|
|
1219
|
+
await chmod5(configuration.databasePath, 384);
|
|
1220
1220
|
}
|
|
1221
1221
|
const hub = new Hub;
|
|
1222
1222
|
let connections = 0;
|
|
@@ -1570,9 +1570,9 @@ var init_main = __esm(async () => {
|
|
|
1570
1570
|
init_config();
|
|
1571
1571
|
init_hub();
|
|
1572
1572
|
init_store();
|
|
1573
|
-
RequestError = class RequestError extends
|
|
1574
|
-
status:
|
|
1575
|
-
message:
|
|
1573
|
+
RequestError = class RequestError extends Schema11.TaggedErrorClass()("Relay.RequestError", {
|
|
1574
|
+
status: Schema11.Int,
|
|
1575
|
+
message: Schema11.String
|
|
1576
1576
|
}) {
|
|
1577
1577
|
};
|
|
1578
1578
|
if (false) {}
|
|
@@ -1738,9 +1738,11 @@ init_src();
|
|
|
1738
1738
|
init_src();
|
|
1739
1739
|
import { Effect as Effect2, Schema as Schema3 } from "effect";
|
|
1740
1740
|
import { accessSync, constants } from "node:fs";
|
|
1741
|
-
import {
|
|
1741
|
+
import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
1742
|
+
import { homedir, platform, tmpdir } from "node:os";
|
|
1742
1743
|
import { delimiter, extname, isAbsolute, join } from "node:path";
|
|
1743
1744
|
import { spawn } from "node:child_process";
|
|
1745
|
+
import { createInterface } from "node:readline";
|
|
1744
1746
|
|
|
1745
1747
|
class ProviderUnavailableError extends Schema3.TaggedErrorClass()("CohallProvider.Unavailable", { provider: Provider, message: Schema3.String }) {
|
|
1746
1748
|
}
|
|
@@ -1878,48 +1880,86 @@ var claudeCommand = (options) => [
|
|
|
1878
1880
|
...options.model === undefined ? [] : ["--model", options.model],
|
|
1879
1881
|
...options.sessionId === undefined ? [] : ["--resume", options.sessionId]
|
|
1880
1882
|
];
|
|
1881
|
-
var opencodeCommand = (options) => [
|
|
1883
|
+
var opencodeCommand = (options, promptPath) => [
|
|
1882
1884
|
"opencode",
|
|
1883
1885
|
"run",
|
|
1884
1886
|
"--format",
|
|
1885
1887
|
"json",
|
|
1886
1888
|
...options.model === undefined ? [] : ["--model", options.model],
|
|
1887
1889
|
...options.sessionId === undefined ? [] : ["--session", options.sessionId],
|
|
1888
|
-
|
|
1890
|
+
"--file",
|
|
1891
|
+
promptPath,
|
|
1892
|
+
"Complete the task described in the attached Cohall prompt file."
|
|
1889
1893
|
];
|
|
1890
|
-
var
|
|
1894
|
+
var prepareCommand = async (options) => {
|
|
1891
1895
|
switch (options.provider) {
|
|
1892
1896
|
case "codex":
|
|
1893
|
-
return
|
|
1897
|
+
return {
|
|
1898
|
+
command: codexCommand(options),
|
|
1899
|
+
input: options.prompt,
|
|
1900
|
+
cleanup: () => Promise.resolve()
|
|
1901
|
+
};
|
|
1894
1902
|
case "claude-code":
|
|
1895
|
-
return
|
|
1896
|
-
|
|
1897
|
-
|
|
1903
|
+
return {
|
|
1904
|
+
command: claudeCommand(options),
|
|
1905
|
+
input: options.prompt,
|
|
1906
|
+
cleanup: () => Promise.resolve()
|
|
1907
|
+
};
|
|
1908
|
+
case "opencode": {
|
|
1909
|
+
const directory = await mkdtemp(join(tmpdir(), "cohall-opencode-"));
|
|
1910
|
+
const promptPath = join(directory, "prompt.md");
|
|
1911
|
+
try {
|
|
1912
|
+
if (platform() !== "win32") {
|
|
1913
|
+
await chmod(directory, 448);
|
|
1914
|
+
}
|
|
1915
|
+
await writeFile(promptPath, options.prompt, { mode: 384 });
|
|
1916
|
+
return {
|
|
1917
|
+
command: opencodeCommand(options, promptPath),
|
|
1918
|
+
cleanup: () => rm(directory, { recursive: true, force: true })
|
|
1919
|
+
};
|
|
1920
|
+
} catch (cause) {
|
|
1921
|
+
await rm(directory, { recursive: true, force: true }).catch(() => {
|
|
1922
|
+
return;
|
|
1923
|
+
});
|
|
1924
|
+
throw cause;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1898
1927
|
}
|
|
1899
1928
|
};
|
|
1900
|
-
var
|
|
1929
|
+
var forEachJsonEvent = async (stdout, onEvent) => {
|
|
1930
|
+
const lines = createInterface({ input: stdout, crlfDelay: Infinity });
|
|
1931
|
+
try {
|
|
1932
|
+
for await (const line of lines) {
|
|
1933
|
+
if (line.trim().length === 0) {
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
if (Buffer.byteLength(line) > 1024 * 1024) {
|
|
1937
|
+
throw new Error("Provider event exceeded 1024 KiB");
|
|
1938
|
+
}
|
|
1939
|
+
const event = record(JSON.parse(line));
|
|
1940
|
+
if (event !== undefined) {
|
|
1941
|
+
onEvent(event);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
} finally {
|
|
1945
|
+
lines.close();
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
var parseCodex = async (stdout, existingSession) => {
|
|
1901
1949
|
let result = "";
|
|
1902
1950
|
let sessionId = existingSession;
|
|
1903
|
-
|
|
1904
|
-
`)) {
|
|
1905
|
-
if (line.trim().length === 0) {
|
|
1906
|
-
continue;
|
|
1907
|
-
}
|
|
1908
|
-
const event = record(JSON.parse(line));
|
|
1909
|
-
if (event === undefined) {
|
|
1910
|
-
continue;
|
|
1911
|
-
}
|
|
1951
|
+
await forEachJsonEvent(stdout, (event) => {
|
|
1912
1952
|
if (text(event.type) === "thread.started") {
|
|
1913
1953
|
sessionId = text(event.thread_id) ?? sessionId;
|
|
1914
1954
|
}
|
|
1915
1955
|
if (text(event.type) !== "item.completed") {
|
|
1916
|
-
|
|
1956
|
+
return;
|
|
1917
1957
|
}
|
|
1918
1958
|
const item = record(event.item);
|
|
1919
1959
|
if (text(item?.type) === "agent_message") {
|
|
1920
1960
|
result = text(item?.text) ?? text(item?.content) ?? result;
|
|
1921
1961
|
}
|
|
1922
|
-
}
|
|
1962
|
+
});
|
|
1923
1963
|
return {
|
|
1924
1964
|
result: result || "Codex completed the task without a text response.",
|
|
1925
1965
|
...sessionId === undefined ? {} : { sessionId }
|
|
@@ -1953,18 +1993,10 @@ var openCodeError = (event) => {
|
|
|
1953
1993
|
}
|
|
1954
1994
|
return message === undefined ? "OpenCode reported an error" : `OpenCode: ${message}`;
|
|
1955
1995
|
};
|
|
1956
|
-
var parseOpenCode = (stdout, existingSession) => {
|
|
1996
|
+
var parseOpenCode = async (stdout, existingSession) => {
|
|
1957
1997
|
let result = "";
|
|
1958
1998
|
let sessionId = existingSession;
|
|
1959
|
-
|
|
1960
|
-
`)) {
|
|
1961
|
-
if (line.trim().length === 0) {
|
|
1962
|
-
continue;
|
|
1963
|
-
}
|
|
1964
|
-
const event = record(JSON.parse(line));
|
|
1965
|
-
if (event === undefined) {
|
|
1966
|
-
continue;
|
|
1967
|
-
}
|
|
1999
|
+
await forEachJsonEvent(stdout, (event) => {
|
|
1968
2000
|
const failure = openCodeError(event);
|
|
1969
2001
|
if (failure !== undefined) {
|
|
1970
2002
|
throw new Error(failure);
|
|
@@ -1974,33 +2006,39 @@ var parseOpenCode = (stdout, existingSession) => {
|
|
|
1974
2006
|
if (content !== undefined && content.length > 0) {
|
|
1975
2007
|
result = content;
|
|
1976
2008
|
}
|
|
1977
|
-
}
|
|
2009
|
+
});
|
|
1978
2010
|
if (result.length === 0) {
|
|
1979
2011
|
throw new Error("OpenCode produced no result; verify its authentication and use a supported project workspace");
|
|
1980
2012
|
}
|
|
1981
2013
|
return { result, ...sessionId === undefined ? {} : { sessionId } };
|
|
1982
2014
|
};
|
|
1983
|
-
var parse = (options, stdout) => {
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2015
|
+
var parse = async (options, stdout, onFailure) => {
|
|
2016
|
+
try {
|
|
2017
|
+
switch (options.provider) {
|
|
2018
|
+
case "codex":
|
|
2019
|
+
return await parseCodex(stdout, options.sessionId);
|
|
2020
|
+
case "claude-code":
|
|
2021
|
+
return parseClaude(await boundedText(stdout, 1024 * 1024, onFailure), options.sessionId);
|
|
2022
|
+
case "opencode":
|
|
2023
|
+
return await parseOpenCode(stdout, options.sessionId);
|
|
2024
|
+
}
|
|
2025
|
+
} catch (cause) {
|
|
2026
|
+
onFailure();
|
|
2027
|
+
throw cause;
|
|
1991
2028
|
}
|
|
1992
2029
|
};
|
|
1993
2030
|
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
1994
2031
|
var run = (options) => Effect2.tryPromise({
|
|
1995
2032
|
try: async (signal) => {
|
|
1996
|
-
const
|
|
1997
|
-
const executable = commandName === undefined ? undefined : findExecutable(commandName);
|
|
2033
|
+
const executable = findExecutable(executables[options.provider]);
|
|
1998
2034
|
if (executable === undefined) {
|
|
1999
2035
|
throw new ProviderUnavailableError({
|
|
2000
2036
|
provider: options.provider,
|
|
2001
2037
|
message: `${executables[options.provider]} is not available on this device`
|
|
2002
2038
|
});
|
|
2003
2039
|
}
|
|
2040
|
+
const prepared = await prepareCommand(options);
|
|
2041
|
+
const [, ...arguments_] = prepared.command;
|
|
2004
2042
|
const child = spawn(executable, arguments_, {
|
|
2005
2043
|
cwd: options.cwd,
|
|
2006
2044
|
env: {
|
|
@@ -2028,37 +2066,46 @@ var run = (options) => Effect2.tryPromise({
|
|
|
2028
2066
|
killTimer.unref();
|
|
2029
2067
|
};
|
|
2030
2068
|
signal.addEventListener("abort", terminate, { once: true });
|
|
2031
|
-
child.stdin.end(
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2069
|
+
child.stdin.end(prepared.input);
|
|
2070
|
+
try {
|
|
2071
|
+
const [result, stderr, exitCode] = await Promise.all([
|
|
2072
|
+
parse(options, child.stdout, terminate),
|
|
2073
|
+
captureText(child.stderr, 64 * 1024),
|
|
2074
|
+
exited
|
|
2075
|
+
]);
|
|
2076
|
+
if (exitCode !== 0) {
|
|
2077
|
+
throw new ProviderRunError({
|
|
2078
|
+
provider: options.provider,
|
|
2079
|
+
message: failureMessage(executables[options.provider], stderr, exitCode),
|
|
2080
|
+
exitCode
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
if (byteLength(result.result) > 131072) {
|
|
2084
|
+
throw new ProviderRunError({
|
|
2085
|
+
provider: options.provider,
|
|
2086
|
+
message: "Provider result exceeded 128 KiB"
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
if (result.sessionId !== undefined && byteLength(result.sessionId) > 4096) {
|
|
2090
|
+
throw new ProviderRunError({
|
|
2091
|
+
provider: options.provider,
|
|
2092
|
+
message: "Provider session ID exceeded 4 KiB"
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
return result;
|
|
2096
|
+
} finally {
|
|
2097
|
+
signal.removeEventListener("abort", terminate);
|
|
2098
|
+
if (child.exitCode === null) {
|
|
2099
|
+
terminate();
|
|
2100
|
+
await exited.catch(() => {
|
|
2101
|
+
return;
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
if (killTimer !== undefined) {
|
|
2105
|
+
clearTimeout(killTimer);
|
|
2106
|
+
}
|
|
2107
|
+
await prepared.cleanup();
|
|
2060
2108
|
}
|
|
2061
|
-
return result;
|
|
2062
2109
|
},
|
|
2063
2110
|
catch: (cause) => {
|
|
2064
2111
|
if (cause instanceof ProviderUnavailableError || cause instanceof ProviderRunError) {
|
|
@@ -2072,10 +2119,10 @@ var run = (options) => Effect2.tryPromise({
|
|
|
2072
2119
|
});
|
|
2073
2120
|
|
|
2074
2121
|
// apps/device/src/cli.ts
|
|
2075
|
-
import { Effect as Effect5, Schema as
|
|
2076
|
-
import { access as access2, mkdir as
|
|
2122
|
+
import { Effect as Effect5, Schema as Schema7 } from "effect";
|
|
2123
|
+
import { access as access2, mkdir as mkdir3, readFile as readFile3, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
2077
2124
|
import { homedir as homedir3 } from "node:os";
|
|
2078
|
-
import { dirname as
|
|
2125
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2079
2126
|
|
|
2080
2127
|
// skills/cohall/SKILL.md
|
|
2081
2128
|
var SKILL_default = `---
|
|
@@ -2214,7 +2261,7 @@ Return the diagnostic failure to the user rather than claiming remote work ran.
|
|
|
2214
2261
|
// apps/device/src/config.ts
|
|
2215
2262
|
init_src();
|
|
2216
2263
|
import { Effect as Effect3, Schema as Schema4 } from "effect";
|
|
2217
|
-
import { access, chmod, mkdir, readFile, realpath, rename, writeFile } from "node:fs/promises";
|
|
2264
|
+
import { access, chmod as chmod2, mkdir, readFile, realpath, rename, writeFile as writeFile2 } from "node:fs/promises";
|
|
2218
2265
|
import { homedir as homedir2, hostname, platform as platform2 } from "node:os";
|
|
2219
2266
|
import { dirname, join as join2, resolve } from "node:path";
|
|
2220
2267
|
var WorkspaceList = Schema4.Array(Schema4.NonEmptyString).check(Schema4.isMaxLength(64));
|
|
@@ -2288,7 +2335,7 @@ var readStoredConfiguration = async () => {
|
|
|
2288
2335
|
return;
|
|
2289
2336
|
}
|
|
2290
2337
|
if (platform2() !== "win32") {
|
|
2291
|
-
await
|
|
2338
|
+
await chmod2(path, 384);
|
|
2292
2339
|
}
|
|
2293
2340
|
try {
|
|
2294
2341
|
return Schema4.decodeUnknownSync(StoredConfiguration)(JSON.parse(await readFile(path, "utf8")));
|
|
@@ -2302,24 +2349,35 @@ var writeStoredConfiguration = async (configuration) => {
|
|
|
2302
2349
|
const temporary = `${path}.${process.pid}.tmp`;
|
|
2303
2350
|
await mkdir(directory, { recursive: true, mode: 448 });
|
|
2304
2351
|
if (platform2() !== "win32") {
|
|
2305
|
-
await
|
|
2352
|
+
await chmod2(directory, 448);
|
|
2306
2353
|
}
|
|
2307
|
-
await
|
|
2354
|
+
await writeFile2(temporary, `${JSON.stringify(configuration, null, 2)}
|
|
2308
2355
|
`, { mode: 384 });
|
|
2309
2356
|
await rename(temporary, path);
|
|
2310
2357
|
if (platform2() !== "win32") {
|
|
2311
|
-
await
|
|
2358
|
+
await chmod2(path, 384);
|
|
2312
2359
|
}
|
|
2313
2360
|
};
|
|
2361
|
+
var credentialsForRelay = (configuration, relayUrl) => {
|
|
2362
|
+
if (configuration?.relayUrl !== normalizeRelayUrl(relayUrl)) {
|
|
2363
|
+
return {};
|
|
2364
|
+
}
|
|
2365
|
+
return {
|
|
2366
|
+
...configuration.clientToken === undefined ? {} : { clientToken: configuration.clientToken },
|
|
2367
|
+
...configuration.deviceToken === undefined ? {} : { deviceToken: configuration.deviceToken }
|
|
2368
|
+
};
|
|
2369
|
+
};
|
|
2314
2370
|
var makeStoredConfiguration = async (input) => {
|
|
2315
2371
|
const existing = await readStoredConfiguration();
|
|
2372
|
+
const relayUrl = normalizeRelayUrl(input.relayUrl);
|
|
2373
|
+
const retainedCredentials = credentialsForRelay(existing, relayUrl);
|
|
2316
2374
|
const canonicalWorkspaces = input.workspaces === undefined ? existing?.workspaces ?? [] : await Effect3.runPromise(parseWorkspaces("", JSON.stringify(input.workspaces)));
|
|
2317
|
-
const clientToken = input.clientToken ??
|
|
2318
|
-
const deviceToken = input.deviceToken ??
|
|
2375
|
+
const clientToken = input.clientToken ?? retainedCredentials.clientToken;
|
|
2376
|
+
const deviceToken = input.deviceToken ?? retainedCredentials.deviceToken;
|
|
2319
2377
|
const providers = input.providers === "auto" ? undefined : input.providers ?? existing?.providers;
|
|
2320
2378
|
return StoredConfiguration.make({
|
|
2321
2379
|
version: 1,
|
|
2322
|
-
relayUrl
|
|
2380
|
+
relayUrl,
|
|
2323
2381
|
deviceId: existing?.deviceId ?? makeDeviceId(),
|
|
2324
2382
|
deviceName: input.deviceName ?? existing?.deviceName ?? hostname(),
|
|
2325
2383
|
workspaces: canonicalWorkspaces,
|
|
@@ -2360,12 +2418,13 @@ var storedOrDefaults = async () => {
|
|
|
2360
2418
|
var loadClientConfiguration = Effect3.tryPromise({
|
|
2361
2419
|
try: async () => {
|
|
2362
2420
|
const stored = await storedOrDefaults();
|
|
2363
|
-
const
|
|
2421
|
+
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? stored.relayUrl);
|
|
2422
|
+
const token = process.env.COHALL_CLIENT_TOKEN ?? credentialsForRelay(stored, relayUrl).clientToken;
|
|
2364
2423
|
if (token === undefined) {
|
|
2365
2424
|
throw new Error(`No client credential. Run cohall join with a pairing token on stdin or set COHALL_CLIENT_TOKEN.`);
|
|
2366
2425
|
}
|
|
2367
2426
|
return ClientConfiguration.make({
|
|
2368
|
-
relayUrl
|
|
2427
|
+
relayUrl,
|
|
2369
2428
|
token,
|
|
2370
2429
|
...process.env.COHALL_THREAD_ID === undefined ? {} : { mcpThreadId: process.env.COHALL_THREAD_ID }
|
|
2371
2430
|
});
|
|
@@ -2389,7 +2448,8 @@ var loadOwnerConfiguration = Effect3.tryPromise({
|
|
|
2389
2448
|
var loadDeviceConfiguration = Effect3.tryPromise({
|
|
2390
2449
|
try: async () => {
|
|
2391
2450
|
const stored = await storedOrDefaults();
|
|
2392
|
-
const
|
|
2451
|
+
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? stored.relayUrl);
|
|
2452
|
+
const token = process.env.COHALL_DEVICE_TOKEN ?? credentialsForRelay(stored, relayUrl).deviceToken;
|
|
2393
2453
|
if (token === undefined) {
|
|
2394
2454
|
throw new Error(`No device credential. Run cohall join with a pairing token on stdin or set COHALL_DEVICE_TOKEN.`);
|
|
2395
2455
|
}
|
|
@@ -2401,7 +2461,7 @@ var loadDeviceConfiguration = Effect3.tryPromise({
|
|
|
2401
2461
|
const sandbox = environmentSandbox() ?? stored.sandbox;
|
|
2402
2462
|
const providers = environmentProviders(stored);
|
|
2403
2463
|
return DeviceConfiguration.make({
|
|
2404
|
-
relayUrl
|
|
2464
|
+
relayUrl,
|
|
2405
2465
|
token,
|
|
2406
2466
|
id: process.env.COHALL_DEVICE_ID === undefined ? stored.deviceId : DeviceId.make(process.env.COHALL_DEVICE_ID),
|
|
2407
2467
|
name: process.env.COHALL_DEVICE_NAME ?? stored.deviceName,
|
|
@@ -2509,6 +2569,322 @@ var threadContext = Effect4.fn("Cohall.threadContext")(function* (client, thread
|
|
|
2509
2569
|
return yield* client.threadContext(threadId);
|
|
2510
2570
|
});
|
|
2511
2571
|
|
|
2572
|
+
// apps/device/src/upgrade.ts
|
|
2573
|
+
import { Schema as Schema6 } from "effect";
|
|
2574
|
+
import { execFile } from "node:child_process";
|
|
2575
|
+
import { chmod as chmod3, mkdir as mkdir2, readFile as readFile2, realpath as realpath2, rename as rename2, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
2576
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
2577
|
+
import { platform as operatingSystem } from "node:os";
|
|
2578
|
+
var packageName = "@akshar5/cohall";
|
|
2579
|
+
var semanticVersion = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
2580
|
+
var PackageManager = Schema6.Literals(["npm", "bun", "pnpm"]);
|
|
2581
|
+
var RestartReceipt = Schema6.Struct({
|
|
2582
|
+
version: Schema6.String,
|
|
2583
|
+
fromVersion: Schema6.String,
|
|
2584
|
+
packageManager: PackageManager,
|
|
2585
|
+
pendingServices: Schema6.Array(Schema6.String),
|
|
2586
|
+
restartedServices: Schema6.Array(Schema6.String)
|
|
2587
|
+
});
|
|
2588
|
+
var normalizePath = (path) => path.replaceAll("\\", "/");
|
|
2589
|
+
var normalizeUpgradeTarget = (target) => {
|
|
2590
|
+
if (target === undefined || target === "latest") {
|
|
2591
|
+
return "latest";
|
|
2592
|
+
}
|
|
2593
|
+
const normalized = target.startsWith("v") ? target.slice(1) : target;
|
|
2594
|
+
if (!semanticVersion.test(normalized)) {
|
|
2595
|
+
throw new Error("--to must be latest or an exact semantic version");
|
|
2596
|
+
}
|
|
2597
|
+
return normalized;
|
|
2598
|
+
};
|
|
2599
|
+
var packageInstallation = (canonicalEntrypoint, entrypoint = canonicalEntrypoint) => {
|
|
2600
|
+
const path = normalizePath(canonicalEntrypoint);
|
|
2601
|
+
const marker = `/node_modules/${packageName}/`;
|
|
2602
|
+
const packageIndex = path.lastIndexOf(marker);
|
|
2603
|
+
if (packageIndex === -1) {
|
|
2604
|
+
throw new Error("cohall upgrade requires a global npm, Bun, or pnpm installation; package-runner and source-checkout executions cannot upgrade themselves");
|
|
2605
|
+
}
|
|
2606
|
+
if (path.includes("/.npm/_npx/") || path.includes("/.bunx/") || path.includes("/pnpm/dlx/") || path.includes("/dlx/")) {
|
|
2607
|
+
throw new Error("This Cohall process is running from a temporary package-runner cache; install it globally before using cohall upgrade");
|
|
2608
|
+
}
|
|
2609
|
+
if (path.includes("/.bun/install/global/node_modules/")) {
|
|
2610
|
+
return { manager: "bun", entrypoint };
|
|
2611
|
+
}
|
|
2612
|
+
if (path.includes("/pnpm/global/") || path.includes("/.pnpm/")) {
|
|
2613
|
+
return { manager: "pnpm", entrypoint };
|
|
2614
|
+
}
|
|
2615
|
+
const nodeModulesParent = path.slice(0, packageIndex);
|
|
2616
|
+
const prefix = nodeModulesParent.endsWith("/lib") ? nodeModulesParent.slice(0, -"/lib".length) : nodeModulesParent;
|
|
2617
|
+
return { manager: "npm", prefix, entrypoint };
|
|
2618
|
+
};
|
|
2619
|
+
var packageInstallCommand = (installation, target) => {
|
|
2620
|
+
const specification = `${packageName}@${target}`;
|
|
2621
|
+
switch (installation.manager) {
|
|
2622
|
+
case "bun":
|
|
2623
|
+
return { command: "bun", arguments: ["add", "--global", specification] };
|
|
2624
|
+
case "pnpm":
|
|
2625
|
+
return { command: "pnpm", arguments: ["add", "--global", specification] };
|
|
2626
|
+
case "npm":
|
|
2627
|
+
return {
|
|
2628
|
+
command: "npm",
|
|
2629
|
+
arguments: [
|
|
2630
|
+
"install",
|
|
2631
|
+
"--global",
|
|
2632
|
+
...installation.prefix === undefined ? [] : ["--prefix", installation.prefix],
|
|
2633
|
+
specification
|
|
2634
|
+
]
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
};
|
|
2638
|
+
var serviceCandidates = (runtimePlatform, uid) => {
|
|
2639
|
+
if (runtimePlatform === "linux") {
|
|
2640
|
+
const systemd = (manager, unit, device) => {
|
|
2641
|
+
const user = manager === "systemd-user" ? ["--user"] : [];
|
|
2642
|
+
return {
|
|
2643
|
+
id: `${manager}:${unit}`,
|
|
2644
|
+
label: unit,
|
|
2645
|
+
manager,
|
|
2646
|
+
device,
|
|
2647
|
+
check: { command: "systemctl", arguments: [...user, "is-active", "--quiet", unit] },
|
|
2648
|
+
restart: [{ command: "systemctl", arguments: [...user, "restart", unit] }]
|
|
2649
|
+
};
|
|
2650
|
+
};
|
|
2651
|
+
return [
|
|
2652
|
+
systemd("systemd-user", "cohall-relay.service", false),
|
|
2653
|
+
systemd("systemd-system", "cohall-relay.service", false),
|
|
2654
|
+
systemd("systemd-user", "cohall-device.service", true)
|
|
2655
|
+
];
|
|
2656
|
+
}
|
|
2657
|
+
if (runtimePlatform === "darwin") {
|
|
2658
|
+
const domain = `gui/${uid ?? 0}`;
|
|
2659
|
+
const launchd = (label, device) => ({
|
|
2660
|
+
id: `launchd:${label}`,
|
|
2661
|
+
label,
|
|
2662
|
+
manager: "launchd",
|
|
2663
|
+
device,
|
|
2664
|
+
check: { command: "launchctl", arguments: ["print", `${domain}/${label}`] },
|
|
2665
|
+
activeOutput: /\bstate\s*=\s*running\b/,
|
|
2666
|
+
restart: [{ command: "launchctl", arguments: ["kickstart", "-k", `${domain}/${label}`] }]
|
|
2667
|
+
});
|
|
2668
|
+
return [launchd("com.cohall.relay", false), launchd("com.cohall.device", true)];
|
|
2669
|
+
}
|
|
2670
|
+
if (runtimePlatform === "win32") {
|
|
2671
|
+
return [
|
|
2672
|
+
{
|
|
2673
|
+
id: "scheduled-task:Cohall Device",
|
|
2674
|
+
label: "Cohall Device",
|
|
2675
|
+
manager: "scheduled-task",
|
|
2676
|
+
device: true,
|
|
2677
|
+
check: {
|
|
2678
|
+
command: "powershell.exe",
|
|
2679
|
+
arguments: [
|
|
2680
|
+
"-NoProfile",
|
|
2681
|
+
"-NonInteractive",
|
|
2682
|
+
"-Command",
|
|
2683
|
+
"if ((Get-ScheduledTask -TaskName 'Cohall Device' -ErrorAction SilentlyContinue).State -eq 'Running') { exit 0 } else { exit 1 }"
|
|
2684
|
+
]
|
|
2685
|
+
},
|
|
2686
|
+
restart: [
|
|
2687
|
+
{
|
|
2688
|
+
command: "schtasks",
|
|
2689
|
+
arguments: ["/End", "/TN", "Cohall Device"],
|
|
2690
|
+
allowFailure: true
|
|
2691
|
+
},
|
|
2692
|
+
{ command: "schtasks", arguments: ["/Run", "/TN", "Cohall Device"] }
|
|
2693
|
+
]
|
|
2694
|
+
}
|
|
2695
|
+
];
|
|
2696
|
+
}
|
|
2697
|
+
return [];
|
|
2698
|
+
};
|
|
2699
|
+
var defaultRunner = {
|
|
2700
|
+
run: (command, arguments_, timeoutMs = 300000) => new Promise((resolve2) => {
|
|
2701
|
+
execFile(command, [...arguments_], { encoding: "utf8", maxBuffer: 1024 * 1024, timeout: timeoutMs, windowsHide: true }, (error, stdout, stderr) => {
|
|
2702
|
+
resolve2({
|
|
2703
|
+
exitCode: typeof error?.code === "number" ? error.code : error === null ? 0 : 1,
|
|
2704
|
+
stdout,
|
|
2705
|
+
stderr,
|
|
2706
|
+
...error === null ? {} : { error: error.message }
|
|
2707
|
+
});
|
|
2708
|
+
});
|
|
2709
|
+
})
|
|
2710
|
+
};
|
|
2711
|
+
var checked = async (runner, invocation, timeoutMs) => {
|
|
2712
|
+
const result = await runner.run(invocation.command, invocation.arguments, timeoutMs);
|
|
2713
|
+
if (result.exitCode === 0 || invocation.allowFailure === true) {
|
|
2714
|
+
return result;
|
|
2715
|
+
}
|
|
2716
|
+
const detail = (result.stderr.trim() || result.stdout.trim() || result.error || "unknown error").slice(0, 16384);
|
|
2717
|
+
throw new Error(`${invocation.command} ${invocation.arguments.join(" ")} failed with status ${result.exitCode}: ${detail}`);
|
|
2718
|
+
};
|
|
2719
|
+
var activeServices = async (runner, candidates) => {
|
|
2720
|
+
const active = [];
|
|
2721
|
+
for (const service of candidates) {
|
|
2722
|
+
const result = await runner.run(service.check.command, service.check.arguments, 1e4);
|
|
2723
|
+
if (result.exitCode === 0 && (service.activeOutput === undefined || service.activeOutput.test(`${result.stdout}
|
|
2724
|
+
${result.stderr}`))) {
|
|
2725
|
+
active.push(service);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
return active;
|
|
2729
|
+
};
|
|
2730
|
+
var readReceipt = async (path) => readFile2(path, "utf8").then((content) => Schema6.decodeUnknownOption(RestartReceipt)(JSON.parse(content))).then((decoded) => decoded._tag === "Some" ? decoded.value : undefined).catch(() => {
|
|
2731
|
+
return;
|
|
2732
|
+
});
|
|
2733
|
+
var writeReceipt = async (path, value) => {
|
|
2734
|
+
const directory = dirname2(path);
|
|
2735
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
2736
|
+
await mkdir2(directory, { recursive: true, mode: 448 });
|
|
2737
|
+
if (operatingSystem() !== "win32") {
|
|
2738
|
+
await chmod3(directory, 448);
|
|
2739
|
+
}
|
|
2740
|
+
await writeFile3(temporary, `${JSON.stringify(value, null, 2)}
|
|
2741
|
+
`, { mode: 384 });
|
|
2742
|
+
await rename2(temporary, path);
|
|
2743
|
+
};
|
|
2744
|
+
var installedVersion = async (entrypoint) => {
|
|
2745
|
+
const canonicalEntrypoint = await realpath2(entrypoint);
|
|
2746
|
+
const metadata = JSON.parse(await readFile2(join3(dirname2(dirname2(canonicalEntrypoint)), "package.json"), "utf8"));
|
|
2747
|
+
if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || metadata.name !== packageName || !("version" in metadata) || typeof metadata.version !== "string") {
|
|
2748
|
+
throw new Error(`Could not verify the installed ${packageName} version`);
|
|
2749
|
+
}
|
|
2750
|
+
return metadata.version;
|
|
2751
|
+
};
|
|
2752
|
+
var restartReceiptPath = () => join3(dirname2(configurationPath()), "upgrade-restart.json");
|
|
2753
|
+
var restartServices = async (runner, services, statePath, state) => {
|
|
2754
|
+
let current = state;
|
|
2755
|
+
for (const service of services) {
|
|
2756
|
+
for (const invocation of service.restart) {
|
|
2757
|
+
await checked(runner, invocation, 60000);
|
|
2758
|
+
}
|
|
2759
|
+
current = {
|
|
2760
|
+
...current,
|
|
2761
|
+
pendingServices: current.pendingServices.filter((id) => id !== service.id),
|
|
2762
|
+
restartedServices: [...current.restartedServices, service.id]
|
|
2763
|
+
};
|
|
2764
|
+
await writeReceipt(statePath, current);
|
|
2765
|
+
}
|
|
2766
|
+
return current;
|
|
2767
|
+
};
|
|
2768
|
+
var upgrade = async (options) => {
|
|
2769
|
+
const runner = options.runner ?? defaultRunner;
|
|
2770
|
+
const runtimePlatform = options.platform ?? process.platform;
|
|
2771
|
+
const uid = options.uid ?? process.getuid?.();
|
|
2772
|
+
const candidates = serviceCandidates(runtimePlatform, uid);
|
|
2773
|
+
const statePath = options.statePath ?? restartReceiptPath();
|
|
2774
|
+
const previous = await readReceipt(statePath);
|
|
2775
|
+
if (previous?.version === options.currentVersion) {
|
|
2776
|
+
const byId = new Map(candidates.map((service) => [service.id, service]));
|
|
2777
|
+
const pending = previous.pendingServices.flatMap((id) => {
|
|
2778
|
+
const service = byId.get(id);
|
|
2779
|
+
return service === undefined ? [] : [service];
|
|
2780
|
+
});
|
|
2781
|
+
const active = await activeServices(runner, pending);
|
|
2782
|
+
if (options.dryRun || !options.restart) {
|
|
2783
|
+
return {
|
|
2784
|
+
upgraded: previous.fromVersion !== previous.version,
|
|
2785
|
+
from_version: previous.fromVersion,
|
|
2786
|
+
installed_version: previous.version,
|
|
2787
|
+
requested_version: previous.version,
|
|
2788
|
+
package_manager: previous.packageManager,
|
|
2789
|
+
services_restarted: previous.restartedServices,
|
|
2790
|
+
services_pending_restart: active.map((service) => service.id),
|
|
2791
|
+
resumed_after_restart: false,
|
|
2792
|
+
dry_run: options.dryRun
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2795
|
+
const activeIds = new Set(active.map((service) => service.id));
|
|
2796
|
+
const completedByRestart = options.delegated === true ? pending.filter((service) => service.device && activeIds.has(service.id)) : [];
|
|
2797
|
+
const completedIds = new Set(completedByRestart.map((service) => service.id));
|
|
2798
|
+
const remaining = active.filter((service) => !completedIds.has(service.id));
|
|
2799
|
+
let resumed = {
|
|
2800
|
+
...previous,
|
|
2801
|
+
pendingServices: previous.pendingServices.filter((id) => !completedIds.has(id)),
|
|
2802
|
+
restartedServices: [
|
|
2803
|
+
...previous.restartedServices,
|
|
2804
|
+
...completedByRestart.map((service) => service.id)
|
|
2805
|
+
]
|
|
2806
|
+
};
|
|
2807
|
+
if (remaining.length > 0) {
|
|
2808
|
+
await writeReceipt(statePath, resumed);
|
|
2809
|
+
resumed = await restartServices(runner, remaining, statePath, resumed);
|
|
2810
|
+
}
|
|
2811
|
+
await rm2(statePath, { force: true });
|
|
2812
|
+
return {
|
|
2813
|
+
upgraded: previous.fromVersion !== previous.version,
|
|
2814
|
+
from_version: previous.fromVersion,
|
|
2815
|
+
installed_version: previous.version,
|
|
2816
|
+
requested_version: previous.version,
|
|
2817
|
+
package_manager: previous.packageManager,
|
|
2818
|
+
services_restarted: resumed.restartedServices,
|
|
2819
|
+
services_pending_restart: [],
|
|
2820
|
+
resumed_after_restart: true,
|
|
2821
|
+
dry_run: false
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
await rm2(statePath, { force: true });
|
|
2825
|
+
const target = normalizeUpgradeTarget(options.target);
|
|
2826
|
+
const entrypoint = options.entrypoint ?? process.argv[1];
|
|
2827
|
+
if (entrypoint === undefined) {
|
|
2828
|
+
throw new Error("Could not resolve the Cohall executable path");
|
|
2829
|
+
}
|
|
2830
|
+
const installation = packageInstallation(await realpath2(entrypoint), entrypoint);
|
|
2831
|
+
const install = packageInstallCommand(installation, target);
|
|
2832
|
+
const services = await activeServices(runner, candidates);
|
|
2833
|
+
if (options.dryRun) {
|
|
2834
|
+
return {
|
|
2835
|
+
upgraded: false,
|
|
2836
|
+
from_version: options.currentVersion,
|
|
2837
|
+
installed_version: options.currentVersion,
|
|
2838
|
+
requested_version: target,
|
|
2839
|
+
package_manager: installation.manager,
|
|
2840
|
+
services_restarted: [],
|
|
2841
|
+
services_pending_restart: options.restart ? services.map((service) => service.id) : [],
|
|
2842
|
+
resumed_after_restart: false,
|
|
2843
|
+
dry_run: true
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
await checked(runner, install);
|
|
2847
|
+
const nextVersion = await installedVersion(entrypoint);
|
|
2848
|
+
if (target !== "latest" && nextVersion !== target) {
|
|
2849
|
+
throw new Error(`Installed Cohall ${nextVersion}, expected ${target}`);
|
|
2850
|
+
}
|
|
2851
|
+
const upgraded = nextVersion !== options.currentVersion;
|
|
2852
|
+
if (!upgraded || !options.restart || services.length === 0) {
|
|
2853
|
+
return {
|
|
2854
|
+
upgraded,
|
|
2855
|
+
from_version: options.currentVersion,
|
|
2856
|
+
installed_version: nextVersion,
|
|
2857
|
+
requested_version: target,
|
|
2858
|
+
package_manager: installation.manager,
|
|
2859
|
+
services_restarted: [],
|
|
2860
|
+
services_pending_restart: upgraded && !options.restart ? services.map((service) => service.id) : [],
|
|
2861
|
+
resumed_after_restart: false,
|
|
2862
|
+
dry_run: false
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
const initial = {
|
|
2866
|
+
version: nextVersion,
|
|
2867
|
+
fromVersion: options.currentVersion,
|
|
2868
|
+
packageManager: installation.manager,
|
|
2869
|
+
pendingServices: services.map((service) => service.id),
|
|
2870
|
+
restartedServices: []
|
|
2871
|
+
};
|
|
2872
|
+
await writeReceipt(statePath, initial);
|
|
2873
|
+
const completed = await restartServices(runner, services, statePath, initial);
|
|
2874
|
+
await rm2(statePath, { force: true });
|
|
2875
|
+
return {
|
|
2876
|
+
upgraded: true,
|
|
2877
|
+
from_version: options.currentVersion,
|
|
2878
|
+
installed_version: nextVersion,
|
|
2879
|
+
requested_version: target,
|
|
2880
|
+
package_manager: installation.manager,
|
|
2881
|
+
services_restarted: completed.restartedServices,
|
|
2882
|
+
services_pending_restart: [],
|
|
2883
|
+
resumed_after_restart: false,
|
|
2884
|
+
dry_run: false
|
|
2885
|
+
};
|
|
2886
|
+
};
|
|
2887
|
+
|
|
2512
2888
|
// apps/device/src/cli.ts
|
|
2513
2889
|
var valueOptions = new Set([
|
|
2514
2890
|
"context",
|
|
@@ -2526,9 +2902,10 @@ var valueOptions = new Set([
|
|
|
2526
2902
|
"thread",
|
|
2527
2903
|
"timeout",
|
|
2528
2904
|
"token-file",
|
|
2905
|
+
"to",
|
|
2529
2906
|
"workspace"
|
|
2530
2907
|
]);
|
|
2531
|
-
var flagOptions = new Set(["client-only", "follow", "no-wait"]);
|
|
2908
|
+
var flagOptions = new Set(["client-only", "dry-run", "follow", "no-restart", "no-wait"]);
|
|
2532
2909
|
var aliases = new Map([
|
|
2533
2910
|
["-c", "context"],
|
|
2534
2911
|
["-p", "prompt"],
|
|
@@ -2556,6 +2933,7 @@ Usage:
|
|
|
2556
2933
|
cohall sessions
|
|
2557
2934
|
cohall revoke <session-id>
|
|
2558
2935
|
cohall doctor
|
|
2936
|
+
cohall upgrade [--to version|latest] [--no-restart] [--dry-run]
|
|
2559
2937
|
cohall device
|
|
2560
2938
|
cohall relay
|
|
2561
2939
|
cohall mcp
|
|
@@ -2628,9 +3006,9 @@ var allowOptions = (arguments_, names) => {
|
|
|
2628
3006
|
}
|
|
2629
3007
|
}
|
|
2630
3008
|
};
|
|
2631
|
-
var noPositionals = (arguments_,
|
|
3009
|
+
var noPositionals = (arguments_, command) => {
|
|
2632
3010
|
if (arguments_.positionals.length > 0) {
|
|
2633
|
-
throw new Error(`${
|
|
3011
|
+
throw new Error(`${command} does not accept positional arguments`);
|
|
2634
3012
|
}
|
|
2635
3013
|
};
|
|
2636
3014
|
var identifier = (arguments_, label) => {
|
|
@@ -2658,7 +3036,7 @@ var readInput = async (arguments_, name) => {
|
|
|
2658
3036
|
if ((await stat(path)).size > 131072) {
|
|
2659
3037
|
throw new Error(`${name} file exceeds 128 KiB`);
|
|
2660
3038
|
}
|
|
2661
|
-
return
|
|
3039
|
+
return readFile3(path, "utf8");
|
|
2662
3040
|
}
|
|
2663
3041
|
const result = direct === "-" ? await readStdin(131072, name) : direct;
|
|
2664
3042
|
if (result !== undefined && result.length > 131072) {
|
|
@@ -2694,7 +3072,7 @@ var pairingToken = async (arguments_) => {
|
|
|
2694
3072
|
if (path !== undefined && (await stat(path)).size > 512) {
|
|
2695
3073
|
throw new Error("Pairing token file exceeds 512 bytes");
|
|
2696
3074
|
}
|
|
2697
|
-
const token = (path === undefined ? await readStdin(512, "Pairing token") : await
|
|
3075
|
+
const token = (path === undefined ? await readStdin(512, "Pairing token") : await readFile3(path, "utf8")).trim();
|
|
2698
3076
|
if (token.length === 0 || new TextEncoder().encode(token).byteLength > 256) {
|
|
2699
3077
|
throw new Error("Provide a pairing token on stdin or with --token-file");
|
|
2700
3078
|
}
|
|
@@ -2716,25 +3094,38 @@ var ownerClient = async () => {
|
|
|
2716
3094
|
};
|
|
2717
3095
|
var installSkill = async (target) => {
|
|
2718
3096
|
const roots = {
|
|
2719
|
-
agents:
|
|
2720
|
-
claude:
|
|
2721
|
-
opencode:
|
|
3097
|
+
agents: join4(homedir3(), ".agents", "skills", "cohall", "SKILL.md"),
|
|
3098
|
+
claude: join4(homedir3(), ".claude", "skills", "cohall", "SKILL.md"),
|
|
3099
|
+
opencode: join4(homedir3(), ".config", "opencode", "skills", "cohall", "SKILL.md")
|
|
2722
3100
|
};
|
|
2723
3101
|
const selected = target === "all" ? Object.values(roots) : (target in roots) ? [roots[target]] : undefined;
|
|
2724
3102
|
if (selected === undefined) {
|
|
2725
3103
|
throw new Error("Skill target must be agents, claude, opencode, or all");
|
|
2726
3104
|
}
|
|
2727
3105
|
for (const path of selected) {
|
|
2728
|
-
await
|
|
2729
|
-
await
|
|
3106
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
3107
|
+
await writeFile4(path, SKILL_default);
|
|
2730
3108
|
}
|
|
2731
3109
|
return selected;
|
|
2732
3110
|
};
|
|
2733
3111
|
var printHelp = () => console.log(help);
|
|
2734
3112
|
var printSkill = () => console.log(SKILL_default.trimEnd());
|
|
2735
|
-
var runCli = async (
|
|
3113
|
+
var runCli = async (command, raw) => {
|
|
2736
3114
|
const arguments_ = parseArguments(raw);
|
|
2737
|
-
if (
|
|
3115
|
+
if (command === "upgrade") {
|
|
3116
|
+
allowOptions(arguments_, ["dry-run", "no-restart", "to"]);
|
|
3117
|
+
noPositionals(arguments_, command);
|
|
3118
|
+
const target = option(arguments_, "to");
|
|
3119
|
+
print(await upgrade({
|
|
3120
|
+
currentVersion: version,
|
|
3121
|
+
...target === undefined ? {} : { target },
|
|
3122
|
+
restart: !arguments_.options.has("no-restart"),
|
|
3123
|
+
dryRun: arguments_.options.has("dry-run"),
|
|
3124
|
+
delegated: process.env.COHALL_PROVIDER !== undefined
|
|
3125
|
+
}));
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
if (command === "join") {
|
|
2738
3129
|
allowOptions(arguments_, [
|
|
2739
3130
|
"client-only",
|
|
2740
3131
|
"name",
|
|
@@ -2743,7 +3134,7 @@ var runCli = async (command2, raw) => {
|
|
|
2743
3134
|
"token-file",
|
|
2744
3135
|
"workspace"
|
|
2745
3136
|
]);
|
|
2746
|
-
noPositionals(arguments_,
|
|
3137
|
+
noPositionals(arguments_, command);
|
|
2747
3138
|
const token = await pairingToken(arguments_);
|
|
2748
3139
|
const relayUrl = normalizeRelayUrl(option(arguments_, "relay") ?? "http://127.0.0.1:8787");
|
|
2749
3140
|
const existing = await readStoredConfiguration();
|
|
@@ -2785,9 +3176,9 @@ var runCli = async (command2, raw) => {
|
|
|
2785
3176
|
});
|
|
2786
3177
|
return;
|
|
2787
3178
|
}
|
|
2788
|
-
if (
|
|
3179
|
+
if (command === "config") {
|
|
2789
3180
|
allowOptions(arguments_, []);
|
|
2790
|
-
noPositionals(arguments_,
|
|
3181
|
+
noPositionals(arguments_, command);
|
|
2791
3182
|
const configuration2 = await readStoredConfiguration();
|
|
2792
3183
|
print({
|
|
2793
3184
|
config_path: configurationPath(),
|
|
@@ -2806,9 +3197,9 @@ var runCli = async (command2, raw) => {
|
|
|
2806
3197
|
});
|
|
2807
3198
|
return;
|
|
2808
3199
|
}
|
|
2809
|
-
if (
|
|
3200
|
+
if (command === "configure") {
|
|
2810
3201
|
allowOptions(arguments_, ["model", "name", "providers", "relay", "sandbox", "workspace"]);
|
|
2811
|
-
noPositionals(arguments_,
|
|
3202
|
+
noPositionals(arguments_, command);
|
|
2812
3203
|
const existing = await readStoredConfiguration();
|
|
2813
3204
|
const relayUrl = normalizeRelayUrl(option(arguments_, "relay") ?? existing?.relayUrl ?? "http://127.0.0.1:8787");
|
|
2814
3205
|
const supplied = values(arguments_, "workspace");
|
|
@@ -2823,19 +3214,18 @@ var runCli = async (command2, raw) => {
|
|
|
2823
3214
|
deviceId: existing?.deviceId ?? makeDeviceId(),
|
|
2824
3215
|
deviceName: option(arguments_, "name") ?? existing?.deviceName ?? "cohall-device",
|
|
2825
3216
|
workspaces,
|
|
2826
|
-
...existing
|
|
2827
|
-
...existing?.deviceToken === undefined ? {} : { deviceToken: existing.deviceToken },
|
|
3217
|
+
...credentialsForRelay(existing, relayUrl),
|
|
2828
3218
|
...providers === undefined ? {} : { providers },
|
|
2829
3219
|
...model === undefined ? {} : { model },
|
|
2830
3220
|
...(sandbox ?? existing?.sandbox) === undefined ? {} : {
|
|
2831
|
-
sandbox:
|
|
3221
|
+
sandbox: Schema7.decodeUnknownSync(Schema7.Literals(["read-only", "workspace-write", "danger-full-access"]))(sandbox ?? existing?.sandbox)
|
|
2832
3222
|
}
|
|
2833
3223
|
});
|
|
2834
3224
|
await writeStoredConfiguration(configuration2);
|
|
2835
3225
|
print({ configured: true, config_path: configurationPath() });
|
|
2836
3226
|
return;
|
|
2837
3227
|
}
|
|
2838
|
-
if (
|
|
3228
|
+
if (command === "skill") {
|
|
2839
3229
|
allowOptions(arguments_, []);
|
|
2840
3230
|
if (arguments_.positionals.length === 0) {
|
|
2841
3231
|
printSkill();
|
|
@@ -2847,9 +3237,9 @@ var runCli = async (command2, raw) => {
|
|
|
2847
3237
|
print({ installed: await installSkill(arguments_.positionals[1] ?? "all") });
|
|
2848
3238
|
return;
|
|
2849
3239
|
}
|
|
2850
|
-
if (
|
|
3240
|
+
if (command === "integrations") {
|
|
2851
3241
|
allowOptions(arguments_, []);
|
|
2852
|
-
noPositionals(arguments_,
|
|
3242
|
+
noPositionals(arguments_, command);
|
|
2853
3243
|
print({
|
|
2854
3244
|
cli_skill: "npx -y @akshar5/cohall skill install all",
|
|
2855
3245
|
codex_mcp: "codex mcp add cohall -- npx -y @akshar5/cohall mcp",
|
|
@@ -2866,11 +3256,12 @@ var runCli = async (command2, raw) => {
|
|
|
2866
3256
|
});
|
|
2867
3257
|
return;
|
|
2868
3258
|
}
|
|
2869
|
-
if (
|
|
3259
|
+
if (command === "doctor") {
|
|
2870
3260
|
allowOptions(arguments_, []);
|
|
2871
|
-
noPositionals(arguments_,
|
|
3261
|
+
noPositionals(arguments_, command);
|
|
2872
3262
|
const configuration2 = await readStoredConfiguration();
|
|
2873
3263
|
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? configuration2?.relayUrl ?? "http://127.0.0.1:8787");
|
|
3264
|
+
const storedCredentials = credentialsForRelay(configuration2, relayUrl);
|
|
2874
3265
|
const response = await fetch(`${relayUrl}/api/health`, {
|
|
2875
3266
|
signal: AbortSignal.timeout(1e4)
|
|
2876
3267
|
}).catch(() => {
|
|
@@ -2882,8 +3273,8 @@ var runCli = async (command2, raw) => {
|
|
|
2882
3273
|
provider,
|
|
2883
3274
|
findExecutable(provider === "claude-code" ? "claude" : provider) ?? "not found"
|
|
2884
3275
|
]));
|
|
2885
|
-
const clientToken = process.env.COHALL_CLIENT_TOKEN ??
|
|
2886
|
-
const hasDeviceCredential = process.env.COHALL_DEVICE_TOKEN !== undefined ||
|
|
3276
|
+
const clientToken = process.env.COHALL_CLIENT_TOKEN ?? storedCredentials.clientToken;
|
|
3277
|
+
const hasDeviceCredential = process.env.COHALL_DEVICE_TOKEN !== undefined || storedCredentials.deviceToken !== undefined;
|
|
2887
3278
|
const deviceId = process.env.COHALL_DEVICE_ID ?? configuration2?.deviceId;
|
|
2888
3279
|
const currentDevice = response?.ok !== true || clientToken === undefined || deviceId === undefined ? undefined : await Effect5.runPromise(exports_src.make({ baseUrl: relayUrl, token: clientToken }).devices()).then((devices) => devices.find((device) => device.id === deviceId)).catch(() => {
|
|
2889
3280
|
return;
|
|
@@ -2900,7 +3291,7 @@ var runCli = async (command2, raw) => {
|
|
|
2900
3291
|
relay: response?.ok === true ? "reachable" : "unreachable",
|
|
2901
3292
|
relay_url: relayUrl,
|
|
2902
3293
|
config_path: configurationPath(),
|
|
2903
|
-
client_credential: process.env.COHALL_CLIENT_TOKEN !== undefined ||
|
|
3294
|
+
client_credential: process.env.COHALL_CLIENT_TOKEN !== undefined || storedCredentials.clientToken !== undefined,
|
|
2904
3295
|
device_credential: hasDeviceCredential,
|
|
2905
3296
|
workspaces: configuration2?.workspaces ?? [],
|
|
2906
3297
|
providers: providerExecutables,
|
|
@@ -2912,11 +3303,11 @@ var runCli = async (command2, raw) => {
|
|
|
2912
3303
|
});
|
|
2913
3304
|
return;
|
|
2914
3305
|
}
|
|
2915
|
-
if (
|
|
3306
|
+
if (command === "pair" || command === "sessions" || command === "revoke") {
|
|
2916
3307
|
const { configuration: configuration2, relay: relay2 } = await ownerClient();
|
|
2917
|
-
if (
|
|
3308
|
+
if (command === "pair") {
|
|
2918
3309
|
allowOptions(arguments_, ["client-only", "label"]);
|
|
2919
|
-
noPositionals(arguments_,
|
|
3310
|
+
noPositionals(arguments_, command);
|
|
2920
3311
|
const roles = arguments_.options.has("client-only") ? ["client"] : ["client", "device"];
|
|
2921
3312
|
const credential = await Effect5.runPromise(relay2.createPairing({ label: option(arguments_, "label") ?? "Cohall device", roles }));
|
|
2922
3313
|
print({
|
|
@@ -2927,24 +3318,24 @@ var runCli = async (command2, raw) => {
|
|
|
2927
3318
|
});
|
|
2928
3319
|
return;
|
|
2929
3320
|
}
|
|
2930
|
-
if (
|
|
3321
|
+
if (command === "sessions") {
|
|
2931
3322
|
allowOptions(arguments_, []);
|
|
2932
|
-
noPositionals(arguments_,
|
|
3323
|
+
noPositionals(arguments_, command);
|
|
2933
3324
|
print(await Effect5.runPromise(relay2.authSessions()));
|
|
2934
3325
|
return;
|
|
2935
3326
|
}
|
|
2936
3327
|
allowOptions(arguments_, []);
|
|
2937
|
-
print(await Effect5.runPromise(relay2.revokeAuthSession(
|
|
3328
|
+
print(await Effect5.runPromise(relay2.revokeAuthSession(Schema7.decodeUnknownSync(AuthSessionId)(identifier(arguments_, "session id")))));
|
|
2938
3329
|
return;
|
|
2939
3330
|
}
|
|
2940
3331
|
const { configuration, relay } = await client();
|
|
2941
|
-
if (
|
|
3332
|
+
if (command === "devices") {
|
|
2942
3333
|
allowOptions(arguments_, []);
|
|
2943
|
-
noPositionals(arguments_,
|
|
3334
|
+
noPositionals(arguments_, command);
|
|
2944
3335
|
print(await Effect5.runPromise(relay.devices()));
|
|
2945
3336
|
return;
|
|
2946
3337
|
}
|
|
2947
|
-
if (
|
|
3338
|
+
if (command === "delegate") {
|
|
2948
3339
|
allowOptions(arguments_, [
|
|
2949
3340
|
"context",
|
|
2950
3341
|
"context-file",
|
|
@@ -2977,22 +3368,22 @@ var runCli = async (command2, raw) => {
|
|
|
2977
3368
|
prompt,
|
|
2978
3369
|
...target === undefined ? {} : { target },
|
|
2979
3370
|
...context === undefined ? {} : { context },
|
|
2980
|
-
...thread === undefined ? {} : { threadId:
|
|
3371
|
+
...thread === undefined ? {} : { threadId: Schema7.decodeUnknownSync(ThreadId)(thread) },
|
|
2981
3372
|
...workspace === undefined ? {} : { workspace },
|
|
2982
|
-
...provider === undefined ? {} : { provider:
|
|
3373
|
+
...provider === undefined ? {} : { provider: Schema7.decodeUnknownSync(Provider)(provider) }
|
|
2983
3374
|
}));
|
|
2984
3375
|
print(taskResult(arguments_.options.has("no-wait") ? task : await Effect5.runPromise(waitForTask(relay, task, timeout(arguments_)))));
|
|
2985
3376
|
return;
|
|
2986
3377
|
}
|
|
2987
|
-
if (
|
|
3378
|
+
if (command === "status") {
|
|
2988
3379
|
allowOptions(arguments_, []);
|
|
2989
|
-
const id =
|
|
3380
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
2990
3381
|
print(taskResult(await Effect5.runPromise(relay.getTask(id))));
|
|
2991
3382
|
return;
|
|
2992
3383
|
}
|
|
2993
|
-
if (
|
|
3384
|
+
if (command === "trace") {
|
|
2994
3385
|
allowOptions(arguments_, ["follow"]);
|
|
2995
|
-
const id =
|
|
3386
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
2996
3387
|
if (arguments_.options.has("follow")) {
|
|
2997
3388
|
await Effect5.runPromise(followTaskTrace(relay, id, printLine));
|
|
2998
3389
|
return;
|
|
@@ -3000,37 +3391,37 @@ var runCli = async (command2, raw) => {
|
|
|
3000
3391
|
print(await Effect5.runPromise(relay.traceTask(id)));
|
|
3001
3392
|
return;
|
|
3002
3393
|
}
|
|
3003
|
-
if (
|
|
3394
|
+
if (command === "wait") {
|
|
3004
3395
|
allowOptions(arguments_, ["timeout"]);
|
|
3005
|
-
const id =
|
|
3396
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
3006
3397
|
const task = await Effect5.runPromise(relay.getTask(id));
|
|
3007
3398
|
print(taskResult(await Effect5.runPromise(waitForTask(relay, task, timeout(arguments_)))));
|
|
3008
3399
|
return;
|
|
3009
3400
|
}
|
|
3010
|
-
if (
|
|
3401
|
+
if (command === "cancel") {
|
|
3011
3402
|
allowOptions(arguments_, []);
|
|
3012
|
-
const id =
|
|
3403
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
3013
3404
|
print(taskResult(await Effect5.runPromise(relay.cancelTask(id))));
|
|
3014
3405
|
return;
|
|
3015
3406
|
}
|
|
3016
|
-
if (
|
|
3407
|
+
if (command === "thread") {
|
|
3017
3408
|
allowOptions(arguments_, []);
|
|
3018
|
-
const id =
|
|
3409
|
+
const id = Schema7.decodeUnknownSync(ThreadId)(identifier(arguments_, "thread id"));
|
|
3019
3410
|
print(await Effect5.runPromise(threadContext(relay, id)));
|
|
3020
3411
|
return;
|
|
3021
3412
|
}
|
|
3022
|
-
throw new Error(`Unknown Cohall command: ${
|
|
3413
|
+
throw new Error(`Unknown Cohall command: ${command}`);
|
|
3023
3414
|
};
|
|
3024
3415
|
|
|
3025
3416
|
// apps/device/src/daemon.ts
|
|
3026
3417
|
init_src();
|
|
3027
|
-
import { Effect as Effect6, Schedule as Schedule2, Schema as
|
|
3028
|
-
import { realpath as
|
|
3418
|
+
import { Effect as Effect6, Schedule as Schedule2, Schema as Schema8 } from "effect";
|
|
3419
|
+
import { realpath as realpath3 } from "node:fs/promises";
|
|
3029
3420
|
import { arch, hostname as hostname2, platform as platform3 } from "node:os";
|
|
3030
3421
|
import { basename, isAbsolute as isAbsolute2, relative } from "node:path";
|
|
3031
3422
|
import { WebSocket } from "ws";
|
|
3032
3423
|
|
|
3033
|
-
class DeviceConnectionError extends
|
|
3424
|
+
class DeviceConnectionError extends Schema8.TaggedErrorClass()("Device.ConnectionError", { message: Schema8.String }) {
|
|
3034
3425
|
}
|
|
3035
3426
|
var socketUrl = (configuration) => {
|
|
3036
3427
|
const url = new URL(configuration.relayUrl);
|
|
@@ -3067,8 +3458,8 @@ var capabilities = (providers) => {
|
|
|
3067
3458
|
};
|
|
3068
3459
|
var selectProviders = (installed, configured) => configured === undefined ? installed : configured.filter((provider) => installed.includes(provider));
|
|
3069
3460
|
var describeDevice = (configuration, status = "online") => {
|
|
3070
|
-
const
|
|
3071
|
-
const platformName =
|
|
3461
|
+
const operatingSystem2 = platform3();
|
|
3462
|
+
const platformName = operatingSystem2 === "darwin" || operatingSystem2 === "linux" ? operatingSystem2 : operatingSystem2 === "win32" ? "windows" : "unknown";
|
|
3072
3463
|
const installed = availableProviders();
|
|
3073
3464
|
const providers = selectProviders(installed, configuration.providers);
|
|
3074
3465
|
return Device.make({
|
|
@@ -3093,7 +3484,7 @@ var allowedWorkspace = async (configuration, requested) => {
|
|
|
3093
3484
|
if (selected === undefined) {
|
|
3094
3485
|
throw new Error("No workspace is configured on this device");
|
|
3095
3486
|
}
|
|
3096
|
-
const candidate = await
|
|
3487
|
+
const candidate = await realpath3(selected);
|
|
3097
3488
|
const allowed = configuration.workspaces.some((root) => {
|
|
3098
3489
|
const child = relative(root, candidate);
|
|
3099
3490
|
return child === "" || !child.startsWith("..") && !isAbsolute2(child);
|
|
@@ -3396,43 +3787,43 @@ var runMcp = async (configuration) => {
|
|
|
3396
3787
|
|
|
3397
3788
|
// apps/device/src/main.ts
|
|
3398
3789
|
var main = async () => {
|
|
3399
|
-
const
|
|
3400
|
-
if (
|
|
3790
|
+
const command = process.argv[2];
|
|
3791
|
+
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
3401
3792
|
printHelp();
|
|
3402
3793
|
return;
|
|
3403
3794
|
}
|
|
3404
|
-
if (
|
|
3795
|
+
if (command === "--version" || command === "version") {
|
|
3405
3796
|
console.log(version);
|
|
3406
3797
|
return;
|
|
3407
3798
|
}
|
|
3408
|
-
if (["device", "relay", "mcp"].includes(
|
|
3799
|
+
if (["device", "relay", "mcp"].includes(command) && process.argv.length > 3) {
|
|
3409
3800
|
const arguments_ = process.argv.slice(3);
|
|
3410
3801
|
if (arguments_.length === 1 && ["--help", "-h", "help"].includes(arguments_[0] ?? "")) {
|
|
3411
3802
|
printHelp();
|
|
3412
3803
|
return;
|
|
3413
3804
|
}
|
|
3414
|
-
throw new Error(`${
|
|
3805
|
+
throw new Error(`${command} does not accept arguments`);
|
|
3415
3806
|
}
|
|
3416
|
-
if (
|
|
3807
|
+
if (command === "relay") {
|
|
3417
3808
|
const { runRelay: runRelay2 } = await init_main().then(() => exports_main);
|
|
3418
3809
|
await runRelay2();
|
|
3419
3810
|
return;
|
|
3420
3811
|
}
|
|
3421
|
-
if (
|
|
3812
|
+
if (command === "device") {
|
|
3422
3813
|
const configuration = await Effect11.runPromise(loadDeviceConfiguration);
|
|
3423
3814
|
await Effect11.runPromise(runDaemon(configuration));
|
|
3424
3815
|
return;
|
|
3425
3816
|
}
|
|
3426
|
-
if (
|
|
3817
|
+
if (command === "mcp") {
|
|
3427
3818
|
const configuration = await Effect11.runPromise(loadClientConfiguration);
|
|
3428
3819
|
await runMcp(configuration);
|
|
3429
3820
|
return;
|
|
3430
3821
|
}
|
|
3431
|
-
await runCli(
|
|
3822
|
+
await runCli(command, process.argv.slice(3));
|
|
3432
3823
|
};
|
|
3433
3824
|
await main().catch((cause) => {
|
|
3434
3825
|
console.error(JSON.stringify({ error: cause instanceof Error ? cause.message : String(cause) }, null, 2));
|
|
3435
3826
|
process.exitCode = 1;
|
|
3436
3827
|
});
|
|
3437
3828
|
|
|
3438
|
-
//# debugId=
|
|
3829
|
+
//# debugId=C3D1F939F21B764D64756E2164756E21
|