@akshar5/cohall 0.3.4 → 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 +13 -0
- package/README.md +5 -0
- package/bin/cohall.js +537 -154
- 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,7 +1738,8 @@ 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";
|
|
1744
1745
|
import { createInterface } from "node:readline";
|
|
@@ -1879,23 +1880,50 @@ var claudeCommand = (options) => [
|
|
|
1879
1880
|
...options.model === undefined ? [] : ["--model", options.model],
|
|
1880
1881
|
...options.sessionId === undefined ? [] : ["--resume", options.sessionId]
|
|
1881
1882
|
];
|
|
1882
|
-
var opencodeCommand = (options) => [
|
|
1883
|
+
var opencodeCommand = (options, promptPath) => [
|
|
1883
1884
|
"opencode",
|
|
1884
1885
|
"run",
|
|
1885
1886
|
"--format",
|
|
1886
1887
|
"json",
|
|
1887
1888
|
...options.model === undefined ? [] : ["--model", options.model],
|
|
1888
1889
|
...options.sessionId === undefined ? [] : ["--session", options.sessionId],
|
|
1889
|
-
|
|
1890
|
+
"--file",
|
|
1891
|
+
promptPath,
|
|
1892
|
+
"Complete the task described in the attached Cohall prompt file."
|
|
1890
1893
|
];
|
|
1891
|
-
var
|
|
1894
|
+
var prepareCommand = async (options) => {
|
|
1892
1895
|
switch (options.provider) {
|
|
1893
1896
|
case "codex":
|
|
1894
|
-
return
|
|
1897
|
+
return {
|
|
1898
|
+
command: codexCommand(options),
|
|
1899
|
+
input: options.prompt,
|
|
1900
|
+
cleanup: () => Promise.resolve()
|
|
1901
|
+
};
|
|
1895
1902
|
case "claude-code":
|
|
1896
|
-
return
|
|
1897
|
-
|
|
1898
|
-
|
|
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
|
+
}
|
|
1899
1927
|
}
|
|
1900
1928
|
};
|
|
1901
1929
|
var forEachJsonEvent = async (stdout, onEvent) => {
|
|
@@ -2002,14 +2030,15 @@ var parse = async (options, stdout, onFailure) => {
|
|
|
2002
2030
|
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
2003
2031
|
var run = (options) => Effect2.tryPromise({
|
|
2004
2032
|
try: async (signal) => {
|
|
2005
|
-
const
|
|
2006
|
-
const executable = commandName === undefined ? undefined : findExecutable(commandName);
|
|
2033
|
+
const executable = findExecutable(executables[options.provider]);
|
|
2007
2034
|
if (executable === undefined) {
|
|
2008
2035
|
throw new ProviderUnavailableError({
|
|
2009
2036
|
provider: options.provider,
|
|
2010
2037
|
message: `${executables[options.provider]} is not available on this device`
|
|
2011
2038
|
});
|
|
2012
2039
|
}
|
|
2040
|
+
const prepared = await prepareCommand(options);
|
|
2041
|
+
const [, ...arguments_] = prepared.command;
|
|
2013
2042
|
const child = spawn(executable, arguments_, {
|
|
2014
2043
|
cwd: options.cwd,
|
|
2015
2044
|
env: {
|
|
@@ -2037,36 +2066,46 @@ var run = (options) => Effect2.tryPromise({
|
|
|
2037
2066
|
killTimer.unref();
|
|
2038
2067
|
};
|
|
2039
2068
|
signal.addEventListener("abort", terminate, { once: true });
|
|
2040
|
-
child.stdin.end(
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
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();
|
|
2068
2108
|
}
|
|
2069
|
-
return result;
|
|
2070
2109
|
},
|
|
2071
2110
|
catch: (cause) => {
|
|
2072
2111
|
if (cause instanceof ProviderUnavailableError || cause instanceof ProviderRunError) {
|
|
@@ -2080,10 +2119,10 @@ var run = (options) => Effect2.tryPromise({
|
|
|
2080
2119
|
});
|
|
2081
2120
|
|
|
2082
2121
|
// apps/device/src/cli.ts
|
|
2083
|
-
import { Effect as Effect5, Schema as
|
|
2084
|
-
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";
|
|
2085
2124
|
import { homedir as homedir3 } from "node:os";
|
|
2086
|
-
import { dirname as
|
|
2125
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
2087
2126
|
|
|
2088
2127
|
// skills/cohall/SKILL.md
|
|
2089
2128
|
var SKILL_default = `---
|
|
@@ -2222,7 +2261,7 @@ Return the diagnostic failure to the user rather than claiming remote work ran.
|
|
|
2222
2261
|
// apps/device/src/config.ts
|
|
2223
2262
|
init_src();
|
|
2224
2263
|
import { Effect as Effect3, Schema as Schema4 } from "effect";
|
|
2225
|
-
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";
|
|
2226
2265
|
import { homedir as homedir2, hostname, platform as platform2 } from "node:os";
|
|
2227
2266
|
import { dirname, join as join2, resolve } from "node:path";
|
|
2228
2267
|
var WorkspaceList = Schema4.Array(Schema4.NonEmptyString).check(Schema4.isMaxLength(64));
|
|
@@ -2296,7 +2335,7 @@ var readStoredConfiguration = async () => {
|
|
|
2296
2335
|
return;
|
|
2297
2336
|
}
|
|
2298
2337
|
if (platform2() !== "win32") {
|
|
2299
|
-
await
|
|
2338
|
+
await chmod2(path, 384);
|
|
2300
2339
|
}
|
|
2301
2340
|
try {
|
|
2302
2341
|
return Schema4.decodeUnknownSync(StoredConfiguration)(JSON.parse(await readFile(path, "utf8")));
|
|
@@ -2310,24 +2349,35 @@ var writeStoredConfiguration = async (configuration) => {
|
|
|
2310
2349
|
const temporary = `${path}.${process.pid}.tmp`;
|
|
2311
2350
|
await mkdir(directory, { recursive: true, mode: 448 });
|
|
2312
2351
|
if (platform2() !== "win32") {
|
|
2313
|
-
await
|
|
2352
|
+
await chmod2(directory, 448);
|
|
2314
2353
|
}
|
|
2315
|
-
await
|
|
2354
|
+
await writeFile2(temporary, `${JSON.stringify(configuration, null, 2)}
|
|
2316
2355
|
`, { mode: 384 });
|
|
2317
2356
|
await rename(temporary, path);
|
|
2318
2357
|
if (platform2() !== "win32") {
|
|
2319
|
-
await
|
|
2358
|
+
await chmod2(path, 384);
|
|
2359
|
+
}
|
|
2360
|
+
};
|
|
2361
|
+
var credentialsForRelay = (configuration, relayUrl) => {
|
|
2362
|
+
if (configuration?.relayUrl !== normalizeRelayUrl(relayUrl)) {
|
|
2363
|
+
return {};
|
|
2320
2364
|
}
|
|
2365
|
+
return {
|
|
2366
|
+
...configuration.clientToken === undefined ? {} : { clientToken: configuration.clientToken },
|
|
2367
|
+
...configuration.deviceToken === undefined ? {} : { deviceToken: configuration.deviceToken }
|
|
2368
|
+
};
|
|
2321
2369
|
};
|
|
2322
2370
|
var makeStoredConfiguration = async (input) => {
|
|
2323
2371
|
const existing = await readStoredConfiguration();
|
|
2372
|
+
const relayUrl = normalizeRelayUrl(input.relayUrl);
|
|
2373
|
+
const retainedCredentials = credentialsForRelay(existing, relayUrl);
|
|
2324
2374
|
const canonicalWorkspaces = input.workspaces === undefined ? existing?.workspaces ?? [] : await Effect3.runPromise(parseWorkspaces("", JSON.stringify(input.workspaces)));
|
|
2325
|
-
const clientToken = input.clientToken ??
|
|
2326
|
-
const deviceToken = input.deviceToken ??
|
|
2375
|
+
const clientToken = input.clientToken ?? retainedCredentials.clientToken;
|
|
2376
|
+
const deviceToken = input.deviceToken ?? retainedCredentials.deviceToken;
|
|
2327
2377
|
const providers = input.providers === "auto" ? undefined : input.providers ?? existing?.providers;
|
|
2328
2378
|
return StoredConfiguration.make({
|
|
2329
2379
|
version: 1,
|
|
2330
|
-
relayUrl
|
|
2380
|
+
relayUrl,
|
|
2331
2381
|
deviceId: existing?.deviceId ?? makeDeviceId(),
|
|
2332
2382
|
deviceName: input.deviceName ?? existing?.deviceName ?? hostname(),
|
|
2333
2383
|
workspaces: canonicalWorkspaces,
|
|
@@ -2368,12 +2418,13 @@ var storedOrDefaults = async () => {
|
|
|
2368
2418
|
var loadClientConfiguration = Effect3.tryPromise({
|
|
2369
2419
|
try: async () => {
|
|
2370
2420
|
const stored = await storedOrDefaults();
|
|
2371
|
-
const
|
|
2421
|
+
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? stored.relayUrl);
|
|
2422
|
+
const token = process.env.COHALL_CLIENT_TOKEN ?? credentialsForRelay(stored, relayUrl).clientToken;
|
|
2372
2423
|
if (token === undefined) {
|
|
2373
2424
|
throw new Error(`No client credential. Run cohall join with a pairing token on stdin or set COHALL_CLIENT_TOKEN.`);
|
|
2374
2425
|
}
|
|
2375
2426
|
return ClientConfiguration.make({
|
|
2376
|
-
relayUrl
|
|
2427
|
+
relayUrl,
|
|
2377
2428
|
token,
|
|
2378
2429
|
...process.env.COHALL_THREAD_ID === undefined ? {} : { mcpThreadId: process.env.COHALL_THREAD_ID }
|
|
2379
2430
|
});
|
|
@@ -2397,7 +2448,8 @@ var loadOwnerConfiguration = Effect3.tryPromise({
|
|
|
2397
2448
|
var loadDeviceConfiguration = Effect3.tryPromise({
|
|
2398
2449
|
try: async () => {
|
|
2399
2450
|
const stored = await storedOrDefaults();
|
|
2400
|
-
const
|
|
2451
|
+
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? stored.relayUrl);
|
|
2452
|
+
const token = process.env.COHALL_DEVICE_TOKEN ?? credentialsForRelay(stored, relayUrl).deviceToken;
|
|
2401
2453
|
if (token === undefined) {
|
|
2402
2454
|
throw new Error(`No device credential. Run cohall join with a pairing token on stdin or set COHALL_DEVICE_TOKEN.`);
|
|
2403
2455
|
}
|
|
@@ -2409,7 +2461,7 @@ var loadDeviceConfiguration = Effect3.tryPromise({
|
|
|
2409
2461
|
const sandbox = environmentSandbox() ?? stored.sandbox;
|
|
2410
2462
|
const providers = environmentProviders(stored);
|
|
2411
2463
|
return DeviceConfiguration.make({
|
|
2412
|
-
relayUrl
|
|
2464
|
+
relayUrl,
|
|
2413
2465
|
token,
|
|
2414
2466
|
id: process.env.COHALL_DEVICE_ID === undefined ? stored.deviceId : DeviceId.make(process.env.COHALL_DEVICE_ID),
|
|
2415
2467
|
name: process.env.COHALL_DEVICE_NAME ?? stored.deviceName,
|
|
@@ -2517,6 +2569,322 @@ var threadContext = Effect4.fn("Cohall.threadContext")(function* (client, thread
|
|
|
2517
2569
|
return yield* client.threadContext(threadId);
|
|
2518
2570
|
});
|
|
2519
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
|
+
|
|
2520
2888
|
// apps/device/src/cli.ts
|
|
2521
2889
|
var valueOptions = new Set([
|
|
2522
2890
|
"context",
|
|
@@ -2534,9 +2902,10 @@ var valueOptions = new Set([
|
|
|
2534
2902
|
"thread",
|
|
2535
2903
|
"timeout",
|
|
2536
2904
|
"token-file",
|
|
2905
|
+
"to",
|
|
2537
2906
|
"workspace"
|
|
2538
2907
|
]);
|
|
2539
|
-
var flagOptions = new Set(["client-only", "follow", "no-wait"]);
|
|
2908
|
+
var flagOptions = new Set(["client-only", "dry-run", "follow", "no-restart", "no-wait"]);
|
|
2540
2909
|
var aliases = new Map([
|
|
2541
2910
|
["-c", "context"],
|
|
2542
2911
|
["-p", "prompt"],
|
|
@@ -2564,6 +2933,7 @@ Usage:
|
|
|
2564
2933
|
cohall sessions
|
|
2565
2934
|
cohall revoke <session-id>
|
|
2566
2935
|
cohall doctor
|
|
2936
|
+
cohall upgrade [--to version|latest] [--no-restart] [--dry-run]
|
|
2567
2937
|
cohall device
|
|
2568
2938
|
cohall relay
|
|
2569
2939
|
cohall mcp
|
|
@@ -2636,9 +3006,9 @@ var allowOptions = (arguments_, names) => {
|
|
|
2636
3006
|
}
|
|
2637
3007
|
}
|
|
2638
3008
|
};
|
|
2639
|
-
var noPositionals = (arguments_,
|
|
3009
|
+
var noPositionals = (arguments_, command) => {
|
|
2640
3010
|
if (arguments_.positionals.length > 0) {
|
|
2641
|
-
throw new Error(`${
|
|
3011
|
+
throw new Error(`${command} does not accept positional arguments`);
|
|
2642
3012
|
}
|
|
2643
3013
|
};
|
|
2644
3014
|
var identifier = (arguments_, label) => {
|
|
@@ -2666,7 +3036,7 @@ var readInput = async (arguments_, name) => {
|
|
|
2666
3036
|
if ((await stat(path)).size > 131072) {
|
|
2667
3037
|
throw new Error(`${name} file exceeds 128 KiB`);
|
|
2668
3038
|
}
|
|
2669
|
-
return
|
|
3039
|
+
return readFile3(path, "utf8");
|
|
2670
3040
|
}
|
|
2671
3041
|
const result = direct === "-" ? await readStdin(131072, name) : direct;
|
|
2672
3042
|
if (result !== undefined && result.length > 131072) {
|
|
@@ -2702,7 +3072,7 @@ var pairingToken = async (arguments_) => {
|
|
|
2702
3072
|
if (path !== undefined && (await stat(path)).size > 512) {
|
|
2703
3073
|
throw new Error("Pairing token file exceeds 512 bytes");
|
|
2704
3074
|
}
|
|
2705
|
-
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();
|
|
2706
3076
|
if (token.length === 0 || new TextEncoder().encode(token).byteLength > 256) {
|
|
2707
3077
|
throw new Error("Provide a pairing token on stdin or with --token-file");
|
|
2708
3078
|
}
|
|
@@ -2724,25 +3094,38 @@ var ownerClient = async () => {
|
|
|
2724
3094
|
};
|
|
2725
3095
|
var installSkill = async (target) => {
|
|
2726
3096
|
const roots = {
|
|
2727
|
-
agents:
|
|
2728
|
-
claude:
|
|
2729
|
-
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")
|
|
2730
3100
|
};
|
|
2731
3101
|
const selected = target === "all" ? Object.values(roots) : (target in roots) ? [roots[target]] : undefined;
|
|
2732
3102
|
if (selected === undefined) {
|
|
2733
3103
|
throw new Error("Skill target must be agents, claude, opencode, or all");
|
|
2734
3104
|
}
|
|
2735
3105
|
for (const path of selected) {
|
|
2736
|
-
await
|
|
2737
|
-
await
|
|
3106
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
3107
|
+
await writeFile4(path, SKILL_default);
|
|
2738
3108
|
}
|
|
2739
3109
|
return selected;
|
|
2740
3110
|
};
|
|
2741
3111
|
var printHelp = () => console.log(help);
|
|
2742
3112
|
var printSkill = () => console.log(SKILL_default.trimEnd());
|
|
2743
|
-
var runCli = async (
|
|
3113
|
+
var runCli = async (command, raw) => {
|
|
2744
3114
|
const arguments_ = parseArguments(raw);
|
|
2745
|
-
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") {
|
|
2746
3129
|
allowOptions(arguments_, [
|
|
2747
3130
|
"client-only",
|
|
2748
3131
|
"name",
|
|
@@ -2751,7 +3134,7 @@ var runCli = async (command2, raw) => {
|
|
|
2751
3134
|
"token-file",
|
|
2752
3135
|
"workspace"
|
|
2753
3136
|
]);
|
|
2754
|
-
noPositionals(arguments_,
|
|
3137
|
+
noPositionals(arguments_, command);
|
|
2755
3138
|
const token = await pairingToken(arguments_);
|
|
2756
3139
|
const relayUrl = normalizeRelayUrl(option(arguments_, "relay") ?? "http://127.0.0.1:8787");
|
|
2757
3140
|
const existing = await readStoredConfiguration();
|
|
@@ -2793,9 +3176,9 @@ var runCli = async (command2, raw) => {
|
|
|
2793
3176
|
});
|
|
2794
3177
|
return;
|
|
2795
3178
|
}
|
|
2796
|
-
if (
|
|
3179
|
+
if (command === "config") {
|
|
2797
3180
|
allowOptions(arguments_, []);
|
|
2798
|
-
noPositionals(arguments_,
|
|
3181
|
+
noPositionals(arguments_, command);
|
|
2799
3182
|
const configuration2 = await readStoredConfiguration();
|
|
2800
3183
|
print({
|
|
2801
3184
|
config_path: configurationPath(),
|
|
@@ -2814,9 +3197,9 @@ var runCli = async (command2, raw) => {
|
|
|
2814
3197
|
});
|
|
2815
3198
|
return;
|
|
2816
3199
|
}
|
|
2817
|
-
if (
|
|
3200
|
+
if (command === "configure") {
|
|
2818
3201
|
allowOptions(arguments_, ["model", "name", "providers", "relay", "sandbox", "workspace"]);
|
|
2819
|
-
noPositionals(arguments_,
|
|
3202
|
+
noPositionals(arguments_, command);
|
|
2820
3203
|
const existing = await readStoredConfiguration();
|
|
2821
3204
|
const relayUrl = normalizeRelayUrl(option(arguments_, "relay") ?? existing?.relayUrl ?? "http://127.0.0.1:8787");
|
|
2822
3205
|
const supplied = values(arguments_, "workspace");
|
|
@@ -2831,19 +3214,18 @@ var runCli = async (command2, raw) => {
|
|
|
2831
3214
|
deviceId: existing?.deviceId ?? makeDeviceId(),
|
|
2832
3215
|
deviceName: option(arguments_, "name") ?? existing?.deviceName ?? "cohall-device",
|
|
2833
3216
|
workspaces,
|
|
2834
|
-
...existing
|
|
2835
|
-
...existing?.deviceToken === undefined ? {} : { deviceToken: existing.deviceToken },
|
|
3217
|
+
...credentialsForRelay(existing, relayUrl),
|
|
2836
3218
|
...providers === undefined ? {} : { providers },
|
|
2837
3219
|
...model === undefined ? {} : { model },
|
|
2838
3220
|
...(sandbox ?? existing?.sandbox) === undefined ? {} : {
|
|
2839
|
-
sandbox:
|
|
3221
|
+
sandbox: Schema7.decodeUnknownSync(Schema7.Literals(["read-only", "workspace-write", "danger-full-access"]))(sandbox ?? existing?.sandbox)
|
|
2840
3222
|
}
|
|
2841
3223
|
});
|
|
2842
3224
|
await writeStoredConfiguration(configuration2);
|
|
2843
3225
|
print({ configured: true, config_path: configurationPath() });
|
|
2844
3226
|
return;
|
|
2845
3227
|
}
|
|
2846
|
-
if (
|
|
3228
|
+
if (command === "skill") {
|
|
2847
3229
|
allowOptions(arguments_, []);
|
|
2848
3230
|
if (arguments_.positionals.length === 0) {
|
|
2849
3231
|
printSkill();
|
|
@@ -2855,9 +3237,9 @@ var runCli = async (command2, raw) => {
|
|
|
2855
3237
|
print({ installed: await installSkill(arguments_.positionals[1] ?? "all") });
|
|
2856
3238
|
return;
|
|
2857
3239
|
}
|
|
2858
|
-
if (
|
|
3240
|
+
if (command === "integrations") {
|
|
2859
3241
|
allowOptions(arguments_, []);
|
|
2860
|
-
noPositionals(arguments_,
|
|
3242
|
+
noPositionals(arguments_, command);
|
|
2861
3243
|
print({
|
|
2862
3244
|
cli_skill: "npx -y @akshar5/cohall skill install all",
|
|
2863
3245
|
codex_mcp: "codex mcp add cohall -- npx -y @akshar5/cohall mcp",
|
|
@@ -2874,11 +3256,12 @@ var runCli = async (command2, raw) => {
|
|
|
2874
3256
|
});
|
|
2875
3257
|
return;
|
|
2876
3258
|
}
|
|
2877
|
-
if (
|
|
3259
|
+
if (command === "doctor") {
|
|
2878
3260
|
allowOptions(arguments_, []);
|
|
2879
|
-
noPositionals(arguments_,
|
|
3261
|
+
noPositionals(arguments_, command);
|
|
2880
3262
|
const configuration2 = await readStoredConfiguration();
|
|
2881
3263
|
const relayUrl = normalizeRelayUrl(process.env.COHALL_RELAY_URL ?? configuration2?.relayUrl ?? "http://127.0.0.1:8787");
|
|
3264
|
+
const storedCredentials = credentialsForRelay(configuration2, relayUrl);
|
|
2882
3265
|
const response = await fetch(`${relayUrl}/api/health`, {
|
|
2883
3266
|
signal: AbortSignal.timeout(1e4)
|
|
2884
3267
|
}).catch(() => {
|
|
@@ -2890,8 +3273,8 @@ var runCli = async (command2, raw) => {
|
|
|
2890
3273
|
provider,
|
|
2891
3274
|
findExecutable(provider === "claude-code" ? "claude" : provider) ?? "not found"
|
|
2892
3275
|
]));
|
|
2893
|
-
const clientToken = process.env.COHALL_CLIENT_TOKEN ??
|
|
2894
|
-
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;
|
|
2895
3278
|
const deviceId = process.env.COHALL_DEVICE_ID ?? configuration2?.deviceId;
|
|
2896
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(() => {
|
|
2897
3280
|
return;
|
|
@@ -2908,7 +3291,7 @@ var runCli = async (command2, raw) => {
|
|
|
2908
3291
|
relay: response?.ok === true ? "reachable" : "unreachable",
|
|
2909
3292
|
relay_url: relayUrl,
|
|
2910
3293
|
config_path: configurationPath(),
|
|
2911
|
-
client_credential: process.env.COHALL_CLIENT_TOKEN !== undefined ||
|
|
3294
|
+
client_credential: process.env.COHALL_CLIENT_TOKEN !== undefined || storedCredentials.clientToken !== undefined,
|
|
2912
3295
|
device_credential: hasDeviceCredential,
|
|
2913
3296
|
workspaces: configuration2?.workspaces ?? [],
|
|
2914
3297
|
providers: providerExecutables,
|
|
@@ -2920,11 +3303,11 @@ var runCli = async (command2, raw) => {
|
|
|
2920
3303
|
});
|
|
2921
3304
|
return;
|
|
2922
3305
|
}
|
|
2923
|
-
if (
|
|
3306
|
+
if (command === "pair" || command === "sessions" || command === "revoke") {
|
|
2924
3307
|
const { configuration: configuration2, relay: relay2 } = await ownerClient();
|
|
2925
|
-
if (
|
|
3308
|
+
if (command === "pair") {
|
|
2926
3309
|
allowOptions(arguments_, ["client-only", "label"]);
|
|
2927
|
-
noPositionals(arguments_,
|
|
3310
|
+
noPositionals(arguments_, command);
|
|
2928
3311
|
const roles = arguments_.options.has("client-only") ? ["client"] : ["client", "device"];
|
|
2929
3312
|
const credential = await Effect5.runPromise(relay2.createPairing({ label: option(arguments_, "label") ?? "Cohall device", roles }));
|
|
2930
3313
|
print({
|
|
@@ -2935,24 +3318,24 @@ var runCli = async (command2, raw) => {
|
|
|
2935
3318
|
});
|
|
2936
3319
|
return;
|
|
2937
3320
|
}
|
|
2938
|
-
if (
|
|
3321
|
+
if (command === "sessions") {
|
|
2939
3322
|
allowOptions(arguments_, []);
|
|
2940
|
-
noPositionals(arguments_,
|
|
3323
|
+
noPositionals(arguments_, command);
|
|
2941
3324
|
print(await Effect5.runPromise(relay2.authSessions()));
|
|
2942
3325
|
return;
|
|
2943
3326
|
}
|
|
2944
3327
|
allowOptions(arguments_, []);
|
|
2945
|
-
print(await Effect5.runPromise(relay2.revokeAuthSession(
|
|
3328
|
+
print(await Effect5.runPromise(relay2.revokeAuthSession(Schema7.decodeUnknownSync(AuthSessionId)(identifier(arguments_, "session id")))));
|
|
2946
3329
|
return;
|
|
2947
3330
|
}
|
|
2948
3331
|
const { configuration, relay } = await client();
|
|
2949
|
-
if (
|
|
3332
|
+
if (command === "devices") {
|
|
2950
3333
|
allowOptions(arguments_, []);
|
|
2951
|
-
noPositionals(arguments_,
|
|
3334
|
+
noPositionals(arguments_, command);
|
|
2952
3335
|
print(await Effect5.runPromise(relay.devices()));
|
|
2953
3336
|
return;
|
|
2954
3337
|
}
|
|
2955
|
-
if (
|
|
3338
|
+
if (command === "delegate") {
|
|
2956
3339
|
allowOptions(arguments_, [
|
|
2957
3340
|
"context",
|
|
2958
3341
|
"context-file",
|
|
@@ -2985,22 +3368,22 @@ var runCli = async (command2, raw) => {
|
|
|
2985
3368
|
prompt,
|
|
2986
3369
|
...target === undefined ? {} : { target },
|
|
2987
3370
|
...context === undefined ? {} : { context },
|
|
2988
|
-
...thread === undefined ? {} : { threadId:
|
|
3371
|
+
...thread === undefined ? {} : { threadId: Schema7.decodeUnknownSync(ThreadId)(thread) },
|
|
2989
3372
|
...workspace === undefined ? {} : { workspace },
|
|
2990
|
-
...provider === undefined ? {} : { provider:
|
|
3373
|
+
...provider === undefined ? {} : { provider: Schema7.decodeUnknownSync(Provider)(provider) }
|
|
2991
3374
|
}));
|
|
2992
3375
|
print(taskResult(arguments_.options.has("no-wait") ? task : await Effect5.runPromise(waitForTask(relay, task, timeout(arguments_)))));
|
|
2993
3376
|
return;
|
|
2994
3377
|
}
|
|
2995
|
-
if (
|
|
3378
|
+
if (command === "status") {
|
|
2996
3379
|
allowOptions(arguments_, []);
|
|
2997
|
-
const id =
|
|
3380
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
2998
3381
|
print(taskResult(await Effect5.runPromise(relay.getTask(id))));
|
|
2999
3382
|
return;
|
|
3000
3383
|
}
|
|
3001
|
-
if (
|
|
3384
|
+
if (command === "trace") {
|
|
3002
3385
|
allowOptions(arguments_, ["follow"]);
|
|
3003
|
-
const id =
|
|
3386
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
3004
3387
|
if (arguments_.options.has("follow")) {
|
|
3005
3388
|
await Effect5.runPromise(followTaskTrace(relay, id, printLine));
|
|
3006
3389
|
return;
|
|
@@ -3008,37 +3391,37 @@ var runCli = async (command2, raw) => {
|
|
|
3008
3391
|
print(await Effect5.runPromise(relay.traceTask(id)));
|
|
3009
3392
|
return;
|
|
3010
3393
|
}
|
|
3011
|
-
if (
|
|
3394
|
+
if (command === "wait") {
|
|
3012
3395
|
allowOptions(arguments_, ["timeout"]);
|
|
3013
|
-
const id =
|
|
3396
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
3014
3397
|
const task = await Effect5.runPromise(relay.getTask(id));
|
|
3015
3398
|
print(taskResult(await Effect5.runPromise(waitForTask(relay, task, timeout(arguments_)))));
|
|
3016
3399
|
return;
|
|
3017
3400
|
}
|
|
3018
|
-
if (
|
|
3401
|
+
if (command === "cancel") {
|
|
3019
3402
|
allowOptions(arguments_, []);
|
|
3020
|
-
const id =
|
|
3403
|
+
const id = Schema7.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
3021
3404
|
print(taskResult(await Effect5.runPromise(relay.cancelTask(id))));
|
|
3022
3405
|
return;
|
|
3023
3406
|
}
|
|
3024
|
-
if (
|
|
3407
|
+
if (command === "thread") {
|
|
3025
3408
|
allowOptions(arguments_, []);
|
|
3026
|
-
const id =
|
|
3409
|
+
const id = Schema7.decodeUnknownSync(ThreadId)(identifier(arguments_, "thread id"));
|
|
3027
3410
|
print(await Effect5.runPromise(threadContext(relay, id)));
|
|
3028
3411
|
return;
|
|
3029
3412
|
}
|
|
3030
|
-
throw new Error(`Unknown Cohall command: ${
|
|
3413
|
+
throw new Error(`Unknown Cohall command: ${command}`);
|
|
3031
3414
|
};
|
|
3032
3415
|
|
|
3033
3416
|
// apps/device/src/daemon.ts
|
|
3034
3417
|
init_src();
|
|
3035
|
-
import { Effect as Effect6, Schedule as Schedule2, Schema as
|
|
3036
|
-
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";
|
|
3037
3420
|
import { arch, hostname as hostname2, platform as platform3 } from "node:os";
|
|
3038
3421
|
import { basename, isAbsolute as isAbsolute2, relative } from "node:path";
|
|
3039
3422
|
import { WebSocket } from "ws";
|
|
3040
3423
|
|
|
3041
|
-
class DeviceConnectionError extends
|
|
3424
|
+
class DeviceConnectionError extends Schema8.TaggedErrorClass()("Device.ConnectionError", { message: Schema8.String }) {
|
|
3042
3425
|
}
|
|
3043
3426
|
var socketUrl = (configuration) => {
|
|
3044
3427
|
const url = new URL(configuration.relayUrl);
|
|
@@ -3075,8 +3458,8 @@ var capabilities = (providers) => {
|
|
|
3075
3458
|
};
|
|
3076
3459
|
var selectProviders = (installed, configured) => configured === undefined ? installed : configured.filter((provider) => installed.includes(provider));
|
|
3077
3460
|
var describeDevice = (configuration, status = "online") => {
|
|
3078
|
-
const
|
|
3079
|
-
const platformName =
|
|
3461
|
+
const operatingSystem2 = platform3();
|
|
3462
|
+
const platformName = operatingSystem2 === "darwin" || operatingSystem2 === "linux" ? operatingSystem2 : operatingSystem2 === "win32" ? "windows" : "unknown";
|
|
3080
3463
|
const installed = availableProviders();
|
|
3081
3464
|
const providers = selectProviders(installed, configuration.providers);
|
|
3082
3465
|
return Device.make({
|
|
@@ -3101,7 +3484,7 @@ var allowedWorkspace = async (configuration, requested) => {
|
|
|
3101
3484
|
if (selected === undefined) {
|
|
3102
3485
|
throw new Error("No workspace is configured on this device");
|
|
3103
3486
|
}
|
|
3104
|
-
const candidate = await
|
|
3487
|
+
const candidate = await realpath3(selected);
|
|
3105
3488
|
const allowed = configuration.workspaces.some((root) => {
|
|
3106
3489
|
const child = relative(root, candidate);
|
|
3107
3490
|
return child === "" || !child.startsWith("..") && !isAbsolute2(child);
|
|
@@ -3404,43 +3787,43 @@ var runMcp = async (configuration) => {
|
|
|
3404
3787
|
|
|
3405
3788
|
// apps/device/src/main.ts
|
|
3406
3789
|
var main = async () => {
|
|
3407
|
-
const
|
|
3408
|
-
if (
|
|
3790
|
+
const command = process.argv[2];
|
|
3791
|
+
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
3409
3792
|
printHelp();
|
|
3410
3793
|
return;
|
|
3411
3794
|
}
|
|
3412
|
-
if (
|
|
3795
|
+
if (command === "--version" || command === "version") {
|
|
3413
3796
|
console.log(version);
|
|
3414
3797
|
return;
|
|
3415
3798
|
}
|
|
3416
|
-
if (["device", "relay", "mcp"].includes(
|
|
3799
|
+
if (["device", "relay", "mcp"].includes(command) && process.argv.length > 3) {
|
|
3417
3800
|
const arguments_ = process.argv.slice(3);
|
|
3418
3801
|
if (arguments_.length === 1 && ["--help", "-h", "help"].includes(arguments_[0] ?? "")) {
|
|
3419
3802
|
printHelp();
|
|
3420
3803
|
return;
|
|
3421
3804
|
}
|
|
3422
|
-
throw new Error(`${
|
|
3805
|
+
throw new Error(`${command} does not accept arguments`);
|
|
3423
3806
|
}
|
|
3424
|
-
if (
|
|
3807
|
+
if (command === "relay") {
|
|
3425
3808
|
const { runRelay: runRelay2 } = await init_main().then(() => exports_main);
|
|
3426
3809
|
await runRelay2();
|
|
3427
3810
|
return;
|
|
3428
3811
|
}
|
|
3429
|
-
if (
|
|
3812
|
+
if (command === "device") {
|
|
3430
3813
|
const configuration = await Effect11.runPromise(loadDeviceConfiguration);
|
|
3431
3814
|
await Effect11.runPromise(runDaemon(configuration));
|
|
3432
3815
|
return;
|
|
3433
3816
|
}
|
|
3434
|
-
if (
|
|
3817
|
+
if (command === "mcp") {
|
|
3435
3818
|
const configuration = await Effect11.runPromise(loadClientConfiguration);
|
|
3436
3819
|
await runMcp(configuration);
|
|
3437
3820
|
return;
|
|
3438
3821
|
}
|
|
3439
|
-
await runCli(
|
|
3822
|
+
await runCli(command, process.argv.slice(3));
|
|
3440
3823
|
};
|
|
3441
3824
|
await main().catch((cause) => {
|
|
3442
3825
|
console.error(JSON.stringify({ error: cause instanceof Error ? cause.message : String(cause) }, null, 2));
|
|
3443
3826
|
process.exitCode = 1;
|
|
3444
3827
|
});
|
|
3445
3828
|
|
|
3446
|
-
//# debugId=
|
|
3829
|
+
//# debugId=C3D1F939F21B764D64756E2164756E21
|