@akshar5/cohall 0.10.2 → 0.11.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 +7 -0
- package/README.md +3 -1
- package/bin/cohall.js +489 -107
- package/bin/cohall.js.map +8 -7
- package/docs/install.md +15 -3
- package/docs/integrations.md +31 -2
- package/docs/onboarding.md +116 -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 { Effect, Schema } from "effect";
|
|
20
|
-
var version = "0.
|
|
20
|
+
var version = "0.11.0", maxSocketPayloadBytes, maxAttachmentBytes, maxTaskAttachments = 2, maxProgressNoteBytes = 1024, bounded = (maxLength) => Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(maxLength)), optionalText = (maxLength) => Schema.String.check(Schema.isMaxLength(maxLength)), boundedArray = (schema, maxLength) => Schema.Array(schema).check(Schema.isMaxLength(maxLength)), count, uuid = (name) => Schema.String.check(Schema.isUUID(4)).pipe(Schema.brand(name)), isoTimestamp = (name) => Schema.String.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/, {
|
|
21
21
|
expected: "an ISO-8601 UTC timestamp"
|
|
22
22
|
})).pipe(Schema.brand(name)), BotId, Bot, DeviceId, ThreadId, MessageId, TaskId, TaskRequestId, TaskRunId, ClarificationId, AuthSessionId, OperationId, Timestamp, TaskDeadline, taskDeadlineError = "Task deadline exceeded", taskDeadlinePassed = (task, time = Date.now()) => task.expiresAt !== undefined && Date.parse(task.expiresAt) <= time, Platform, Provider, DeviceStatus, TaskStatus, OperationStatus, TaskTraceEventKind, MessageRole, ConnectionRole, AuthSession, CreatePairingInput, PairingCredential, ExchangePairingInput, SessionCredential, PairingResult, Capability, Workspace, Device, maxDevicePageDevices = 16, maxDevicePageResponseBytes, DevicePage, minimumDeviceOperationVersion = "0.5.0", parsedVersion = (value) => {
|
|
23
23
|
const match = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
|
|
@@ -420,25 +420,25 @@ var init_src = __esm(() => {
|
|
|
420
420
|
});
|
|
421
421
|
|
|
422
422
|
// apps/relay/src/config.ts
|
|
423
|
-
import { Effect as Effect13, Schema as
|
|
424
|
-
import { access as access5, chmod as chmod7, mkdir as
|
|
423
|
+
import { Effect as Effect13, Schema as Schema15 } from "effect";
|
|
424
|
+
import { access as access5, chmod as chmod7, mkdir as mkdir9, readFile as readFile4, writeFile as writeFile10 } from "node:fs/promises";
|
|
425
425
|
import { homedir as homedir5, platform as platform5 } from "node:os";
|
|
426
|
-
import { isAbsolute as isAbsolute6, join as
|
|
426
|
+
import { isAbsolute as isAbsolute6, join as join10, resolve as resolve5 } from "node:path";
|
|
427
427
|
var RelayConfiguration, defaultDataDirectory = () => {
|
|
428
428
|
if (platform5() === "win32") {
|
|
429
|
-
return
|
|
429
|
+
return join10(process.env.LOCALAPPDATA ?? join10(homedir5(), "AppData", "Local"), "Cohall");
|
|
430
430
|
}
|
|
431
431
|
if (platform5() === "darwin") {
|
|
432
|
-
return
|
|
432
|
+
return join10(homedir5(), "Library", "Application Support", "Cohall", "relay");
|
|
433
433
|
}
|
|
434
434
|
const dataHome = process.env.XDG_DATA_HOME;
|
|
435
|
-
return
|
|
435
|
+
return join10(dataHome !== undefined && isAbsolute6(dataHome) ? dataHome : join10(homedir5(), ".local", "share"), "cohall");
|
|
436
436
|
}, generatedToken = () => Array.from(crypto.getRandomValues(new Uint8Array(32)), (value) => value.toString(16).padStart(2, "0")).join(""), tokenFrom = async (path) => {
|
|
437
437
|
if (await access5(path).then(() => true).catch(() => false)) {
|
|
438
438
|
return (await readFile4(path, "utf8")).trim();
|
|
439
439
|
}
|
|
440
440
|
const token = generatedToken();
|
|
441
|
-
await
|
|
441
|
+
await writeFile10(path, `${token}
|
|
442
442
|
`, { mode: 384 });
|
|
443
443
|
if (platform5() !== "win32") {
|
|
444
444
|
await chmod7(path, 384);
|
|
@@ -455,14 +455,14 @@ var RelayConfiguration, defaultDataDirectory = () => {
|
|
|
455
455
|
throw new Error(`${name} must be true or false`);
|
|
456
456
|
}, isLoopback2 = (host) => host === "127.0.0.1" || host === "localhost" || host === "::1", loadEnvironmentConfiguration;
|
|
457
457
|
var init_config = __esm(() => {
|
|
458
|
-
RelayConfiguration =
|
|
459
|
-
host:
|
|
460
|
-
port:
|
|
461
|
-
token:
|
|
462
|
-
dataDirectory:
|
|
463
|
-
databasePath:
|
|
464
|
-
allowRemote:
|
|
465
|
-
historyTaskLimit:
|
|
458
|
+
RelayConfiguration = Schema15.Struct({
|
|
459
|
+
host: Schema15.NonEmptyString,
|
|
460
|
+
port: Schema15.Int.check(Schema15.isBetween({ minimum: 1, maximum: 65535 })),
|
|
461
|
+
token: Schema15.String.check(Schema15.isMinLength(32)),
|
|
462
|
+
dataDirectory: Schema15.NonEmptyString,
|
|
463
|
+
databasePath: Schema15.NonEmptyString,
|
|
464
|
+
allowRemote: Schema15.Boolean,
|
|
465
|
+
historyTaskLimit: Schema15.Int.check(Schema15.isBetween({ minimum: 100, maximum: 1e5 }))
|
|
466
466
|
});
|
|
467
467
|
loadEnvironmentConfiguration = Effect13.tryPromise({
|
|
468
468
|
try: async () => {
|
|
@@ -473,21 +473,21 @@ var init_config = __esm(() => {
|
|
|
473
473
|
throw new Error("Remote relay binding is disabled. Use a private network or TLS reverse proxy, then set COHALL_RELAY_ALLOW_REMOTE=true.");
|
|
474
474
|
}
|
|
475
475
|
const dataDirectory = resolve5(process.env.COHALL_DATA_DIR ?? defaultDataDirectory());
|
|
476
|
-
await
|
|
476
|
+
await mkdir9(dataDirectory, { recursive: true, mode: 448 });
|
|
477
477
|
if (platform5() !== "win32") {
|
|
478
478
|
await chmod7(dataDirectory, 448);
|
|
479
479
|
}
|
|
480
|
-
const token = process.env.COHALL_TOKEN ?? await tokenFrom(
|
|
480
|
+
const token = process.env.COHALL_TOKEN ?? await tokenFrom(join10(dataDirectory, "owner-token"));
|
|
481
481
|
if (token.length < 32) {
|
|
482
482
|
throw new Error("The Cohall relay owner token must be at least 32 characters");
|
|
483
483
|
}
|
|
484
484
|
const historyTaskLimit = Number(process.env.COHALL_HISTORY_TASK_LIMIT ?? "1000");
|
|
485
|
-
return
|
|
485
|
+
return Schema15.decodeUnknownSync(RelayConfiguration)({
|
|
486
486
|
host,
|
|
487
487
|
port,
|
|
488
488
|
token,
|
|
489
489
|
dataDirectory,
|
|
490
|
-
databasePath:
|
|
490
|
+
databasePath: join10(dataDirectory, "cohall.db"),
|
|
491
491
|
allowRemote,
|
|
492
492
|
historyTaskLimit
|
|
493
493
|
});
|
|
@@ -673,12 +673,12 @@ __export(exports_store, {
|
|
|
673
673
|
RelayStore: () => exports_store,
|
|
674
674
|
PersistenceError: () => PersistenceError
|
|
675
675
|
});
|
|
676
|
-
import { Context as Context2, Effect as Effect14, Layer as Layer2, Schema as
|
|
676
|
+
import { Context as Context2, Effect as Effect14, Layer as Layer2, Schema as Schema16 } from "effect";
|
|
677
677
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
678
678
|
var PersistenceError, TaskProgressError, TaskInputError, TaskDeadlineError, TaskRequestError, Service2, operationError = (operation) => (cause) => new PersistenceError({
|
|
679
679
|
operation,
|
|
680
680
|
message: cause instanceof Error ? cause.message : String(cause)
|
|
681
|
-
}), decode2 = (operation, schema, value) =>
|
|
681
|
+
}), decode2 = (operation, schema, value) => Schema16.decodeUnknownEffect(schema)(value).pipe(Effect14.mapError(operationError(operation))), threadFromRow = (row) => decode2("RelayStore.decodeThread", Thread, {
|
|
682
682
|
id: row.id,
|
|
683
683
|
title: row.title,
|
|
684
684
|
createdAt: row.created_at,
|
|
@@ -777,7 +777,7 @@ var PersistenceError, TaskProgressError, TaskInputError, TaskDeadlineError, Task
|
|
|
777
777
|
kind: row.kind,
|
|
778
778
|
at: row.created_at,
|
|
779
779
|
...row.detail === null ? {} : { detail: row.detail }
|
|
780
|
-
}), rolesFromRow = (row) =>
|
|
780
|
+
}), rolesFromRow = (row) => Schema16.decodeUnknownSync(Schema16.Array(ConnectionRole))(JSON.parse(row.roles_json)), authSessionFromRow = (row) => {
|
|
781
781
|
const roles = rolesFromRow(row);
|
|
782
782
|
const role = roles[0];
|
|
783
783
|
return role === undefined ? Effect14.fail(new PersistenceError({
|
|
@@ -1115,9 +1115,9 @@ var PersistenceError, TaskProgressError, TaskInputError, TaskDeadlineError, Task
|
|
|
1115
1115
|
const devices = new Map;
|
|
1116
1116
|
let retainedTasks = 0;
|
|
1117
1117
|
for (const row of rows) {
|
|
1118
|
-
const status =
|
|
1119
|
-
const provider =
|
|
1120
|
-
const deviceId =
|
|
1118
|
+
const status = Schema16.decodeUnknownSync(TaskStatus)(row.status);
|
|
1119
|
+
const provider = Schema16.decodeUnknownSync(Provider)(row.provider);
|
|
1120
|
+
const deviceId = Schema16.decodeUnknownSync(DeviceId)(row.device_id);
|
|
1121
1121
|
retainedTasks += row.tasks;
|
|
1122
1122
|
totalStatus[status] += row.tasks;
|
|
1123
1123
|
totalProviders.set(provider, (totalProviders.get(provider) ?? 0) + row.tasks);
|
|
@@ -1909,7 +1909,7 @@ var PersistenceError, TaskProgressError, TaskInputError, TaskDeadlineError, Task
|
|
|
1909
1909
|
if (pairing === null) {
|
|
1910
1910
|
throw new Error("Pairing credential is invalid, expired, or already used");
|
|
1911
1911
|
}
|
|
1912
|
-
const roles =
|
|
1912
|
+
const roles = Schema16.decodeUnknownSync(Schema16.Array(ConnectionRole))(JSON.parse(pairing.roles_json));
|
|
1913
1913
|
const deviceId = roles.includes("device") ? makeDeviceId() : undefined;
|
|
1914
1914
|
const consumed = db.query("UPDATE auth_pairings SET used_at = ? WHERE token_hash = ? AND used_at IS NULL").run(timestamp, pairing.token_hash);
|
|
1915
1915
|
if (consumed.changes !== 1) {
|
|
@@ -2183,19 +2183,19 @@ var init_store = __esm(() => {
|
|
|
2183
2183
|
init_src();
|
|
2184
2184
|
init_database();
|
|
2185
2185
|
init_store();
|
|
2186
|
-
PersistenceError = class PersistenceError extends
|
|
2187
|
-
operation:
|
|
2188
|
-
message:
|
|
2189
|
-
missingRecord:
|
|
2186
|
+
PersistenceError = class PersistenceError extends Schema16.TaggedErrorClass()("RelayStore.PersistenceError", {
|
|
2187
|
+
operation: Schema16.String,
|
|
2188
|
+
message: Schema16.String,
|
|
2189
|
+
missingRecord: Schema16.optionalKey(Schema16.Literals(["task", "operation"]))
|
|
2190
2190
|
}) {
|
|
2191
2191
|
};
|
|
2192
|
-
TaskProgressError = class TaskProgressError extends
|
|
2192
|
+
TaskProgressError = class TaskProgressError extends Schema16.TaggedErrorClass()("RelayStore.TaskProgressError", { status: Schema16.Literals([403, 409]), message: Schema16.String }) {
|
|
2193
2193
|
};
|
|
2194
|
-
TaskInputError = class TaskInputError extends
|
|
2194
|
+
TaskInputError = class TaskInputError extends Schema16.TaggedErrorClass()("RelayStore.TaskInputError", { status: Schema16.Literals([403, 409]), message: Schema16.String }) {
|
|
2195
2195
|
};
|
|
2196
|
-
TaskDeadlineError = class TaskDeadlineError extends
|
|
2196
|
+
TaskDeadlineError = class TaskDeadlineError extends Schema16.TaggedErrorClass()("RelayStore.TaskDeadlineError", { status: Schema16.Literals([400, 409]), message: Schema16.String }) {
|
|
2197
2197
|
};
|
|
2198
|
-
TaskRequestError = class TaskRequestError extends
|
|
2198
|
+
TaskRequestError = class TaskRequestError extends Schema16.TaggedErrorClass()("RelayStore.TaskRequestError", { status: Schema16.Literals([409, 410]), message: Schema16.String }) {
|
|
2199
2199
|
};
|
|
2200
2200
|
Service2 = class Service2 extends Context2.Service()("@cohall/RelayStore") {
|
|
2201
2201
|
};
|
|
@@ -2211,7 +2211,7 @@ __export(exports_main, {
|
|
|
2211
2211
|
canReadTaskAttachments: () => canReadTaskAttachments,
|
|
2212
2212
|
canDispatchTaskToDevice: () => canDispatchTaskToDevice
|
|
2213
2213
|
});
|
|
2214
|
-
import { Effect as Effect15, ManagedRuntime, Schema as
|
|
2214
|
+
import { Effect as Effect15, ManagedRuntime, Schema as Schema17, Schedule as Schedule4 } from "effect";
|
|
2215
2215
|
import { createHash as createHash3, timingSafeEqual } from "node:crypto";
|
|
2216
2216
|
import { access as access6, chmod as chmod8 } from "node:fs/promises";
|
|
2217
2217
|
import { createServer } from "node:http";
|
|
@@ -2314,7 +2314,7 @@ Content-Length: 0\r
|
|
|
2314
2314
|
}, body = (request, decode3) => Effect15.tryPromise({
|
|
2315
2315
|
try: () => request.json(),
|
|
2316
2316
|
catch: () => new RequestError({ status: 400, message: "Expected a JSON request body" })
|
|
2317
|
-
}).pipe(Effect15.flatMap(decode3), Effect15.mapError((cause) => cause instanceof RequestError ? cause : new RequestError({ status: 400, message: String(cause).slice(0, 2048) }))), pathId = (schema, value) =>
|
|
2317
|
+
}).pipe(Effect15.flatMap(decode3), Effect15.mapError((cause) => cause instanceof RequestError ? cause : new RequestError({ status: 400, message: String(cause).slice(0, 2048) }))), pathId = (schema, value) => Schema17.decodeUnknownEffect(schema)(value).pipe(Effect15.mapError(() => new RequestError({ status: 400, message: `Invalid identifier: ${value}` }))), scoreDevice = (device, input, sourceDeviceId) => {
|
|
2318
2318
|
const prompt = `${input.prompt} ${input.context ?? ""}`.toLowerCase();
|
|
2319
2319
|
const needsXcode = /\b(xcode|ios|macos|swiftui|app store)\b/.test(prompt);
|
|
2320
2320
|
const needsBrowser = /\b(browser|chrome|youtube|twitter|logged[- ]?in|signed[- ]?in|x\.com)\b/.test(prompt);
|
|
@@ -3056,9 +3056,9 @@ var init_main = __esm(async () => {
|
|
|
3056
3056
|
init_config();
|
|
3057
3057
|
init_hub();
|
|
3058
3058
|
init_store();
|
|
3059
|
-
RequestError = class RequestError extends
|
|
3060
|
-
status:
|
|
3061
|
-
message:
|
|
3059
|
+
RequestError = class RequestError extends Schema17.TaggedErrorClass()("Relay.RequestError", {
|
|
3060
|
+
status: Schema17.Int,
|
|
3061
|
+
message: Schema17.String
|
|
3062
3062
|
}) {
|
|
3063
3063
|
};
|
|
3064
3064
|
maxTaskRequestBodyBytes = 3 * 1024 * 1024;
|
|
@@ -3903,10 +3903,10 @@ var run = (options) => Effect3.callback((resume, signal) => {
|
|
|
3903
3903
|
});
|
|
3904
3904
|
|
|
3905
3905
|
// apps/device/src/cli.ts
|
|
3906
|
-
import { Effect as Effect10, Schema as
|
|
3907
|
-
import { access as access4, mkdir as
|
|
3906
|
+
import { Effect as Effect10, Schema as Schema12 } from "effect";
|
|
3907
|
+
import { access as access4, mkdir as mkdir8, open as open5, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
3908
3908
|
import { homedir as homedir4 } from "node:os";
|
|
3909
|
-
import { dirname as dirname6, join as
|
|
3909
|
+
import { dirname as dirname6, join as join9, resolve as resolve4 } from "node:path";
|
|
3910
3910
|
|
|
3911
3911
|
// skills/cohall/SKILL.md
|
|
3912
3912
|
var SKILL_default = `---
|
|
@@ -4172,6 +4172,125 @@ cohall devices
|
|
|
4172
4172
|
Return the diagnostic failure to the user rather than claiming remote work ran.
|
|
4173
4173
|
`;
|
|
4174
4174
|
|
|
4175
|
+
// docs/onboarding.md
|
|
4176
|
+
var onboarding_default = `# Set up Cohall with your agent
|
|
4177
|
+
|
|
4178
|
+
These instructions are for the agent doing the setup. Read the path that matches
|
|
4179
|
+
the owner's request, complete the verification steps, and report what works.
|
|
4180
|
+
\`cohall onboard\` prints this same guide without changing the machine.
|
|
4181
|
+
|
|
4182
|
+
## Choose the role
|
|
4183
|
+
|
|
4184
|
+
- A relay address and pairing token mean **join an existing relay**.
|
|
4185
|
+
- A request to host a relay means **host a relay** on an always-on machine.
|
|
4186
|
+
- If neither is clear, ask whether this machine should host or join, and obtain
|
|
4187
|
+
the relay address before pairing. A laptop that sleeps is a poor relay host.
|
|
4188
|
+
- For an existing installation, inspect \`cohall config\` and run \`cohall doctor\`
|
|
4189
|
+
before repairing it. Reuse working credentials and workspace settings.
|
|
4190
|
+
|
|
4191
|
+
Detect the OS, Node.js version, package manager, installed provider CLIs, and
|
|
4192
|
+
existing workspace roots. Cohall requires Node.js 24 or newer. Follow
|
|
4193
|
+
[installation](https://github.com/AksharP5/cohall/blob/main/docs/install.md) for package commands and
|
|
4194
|
+
[services](https://github.com/AksharP5/cohall/blob/main/docs/services.md) for the current OS. Use the owner's authorization for
|
|
4195
|
+
installation and autostart; obtain it when missing. Provider login requires the
|
|
4196
|
+
owner's account and should stay on the machine that runs that provider.
|
|
4197
|
+
|
|
4198
|
+
Keep tokens out of command arguments, shell history, logs, and setup briefs.
|
|
4199
|
+
Read them through a hidden terminal prompt, stdin, or a private token file.
|
|
4200
|
+
|
|
4201
|
+
## Host a relay
|
|
4202
|
+
|
|
4203
|
+
1. Choose a machine that stays awake and a persistent relay data directory.
|
|
4204
|
+
Install Cohall globally so its service has a stable executable path.
|
|
4205
|
+
2. Start \`cohall relay\` once in the foreground. The default listener is
|
|
4206
|
+
\`127.0.0.1:8787\`; the relay creates a private owner token in its data directory
|
|
4207
|
+
when none is supplied. Preserve that directory and include it in backups.
|
|
4208
|
+
3. Make the relay reachable through the owner's private Tailscale network or
|
|
4209
|
+
HTTPS reverse proxy. Confirm the address from a joining machine with
|
|
4210
|
+
\`GET /api/health\`. Plain HTTP is suitable only inside an independently
|
|
4211
|
+
encrypted private network, never as a public listener.
|
|
4212
|
+
4. Configure relay autostart. The packaged relay service instructions cover
|
|
4213
|
+
Linux; macOS and Windows relay hosts need an owner-approved supervisor.
|
|
4214
|
+
\`cohall service install\` installs a **device worker**, not a relay.
|
|
4215
|
+
Prepare the managed relay with the same data and owner configuration. Stop
|
|
4216
|
+
the foreground relay to release its address, start the managed service, and
|
|
4217
|
+
verify relay health before pairing devices.
|
|
4218
|
+
5. On the relay host, create a ten-minute, single-use pairing token for each
|
|
4219
|
+
joining machine:
|
|
4220
|
+
|
|
4221
|
+
\`\`\`bash
|
|
4222
|
+
cohall pair --label "Workstation"
|
|
4223
|
+
\`\`\`
|
|
4224
|
+
|
|
4225
|
+
Set \`COHALL_RELAY_URL\` to the reachable address when creating the pairing so
|
|
4226
|
+
its \`join_instructions\` use that address. The owner token is read from the
|
|
4227
|
+
relay data directory on the host; a service using a different user or data
|
|
4228
|
+
directory needs the matching owner configuration. Transfer the pairing token
|
|
4229
|
+
privately and send the returned join instructions separately.
|
|
4230
|
+
|
|
4231
|
+
6. If this host should also run agent work, pair it as another device and
|
|
4232
|
+
complete the joining path below. Hosting a relay alone does not register a
|
|
4233
|
+
worker or install its agent integration.
|
|
4234
|
+
|
|
4235
|
+
## Join an existing relay
|
|
4236
|
+
|
|
4237
|
+
1. Confirm the relay address and the role. A worker runs providers against its
|
|
4238
|
+
own allowed workspaces; a client-only installation submits work elsewhere.
|
|
4239
|
+
Request a fresh token if the supplied one has expired or was already used.
|
|
4240
|
+
2. Choose existing workspace roots and installed providers for a worker.
|
|
4241
|
+
Confirm provider login on this machine. Use \`--providers codex\`,
|
|
4242
|
+
\`--providers claude-code\`, or \`--providers opencode\` to limit selection;
|
|
4243
|
+
\`auto\` enables every detected provider. Grok Bots have a separate
|
|
4244
|
+
[gateway setup](https://github.com/AksharP5/cohall/blob/main/docs/grok-bot.md).
|
|
4245
|
+
3. Run guided setup with the confirmed address:
|
|
4246
|
+
|
|
4247
|
+
\`\`\`bash
|
|
4248
|
+
cohall init --relay https://your-relay.example
|
|
4249
|
+
\`\`\`
|
|
4250
|
+
|
|
4251
|
+
A terminal prompts for the pairing token without echoing it. Scripts and
|
|
4252
|
+
agents without an interactive terminal supply it on stdin or with
|
|
4253
|
+
\`--token-file\`; see the [pairing examples](https://github.com/AksharP5/cohall/blob/main/docs/install.md#pair-a-machine).
|
|
4254
|
+
Client-only setup adds \`--client-only\` and uses a client-only pairing.
|
|
4255
|
+
\`init\` writes configuration and installs the Cohall skill for Codex,
|
|
4256
|
+
Claude Code, and OpenCode. It preserves existing settings during repair.
|
|
4257
|
+
|
|
4258
|
+
4. For a worker, run \`cohall device\` in the foreground or install its service
|
|
4259
|
+
after a global package installation. \`cohall init --service\` can install the
|
|
4260
|
+
worker service during setup. A client-only installation needs no device
|
|
4261
|
+
service. For Grok Bots, use the gateway instructions to verify availability.
|
|
4262
|
+
5. Use the CLI and installed skill in a harness that can run commands. If the
|
|
4263
|
+
owner prefers MCP, run \`cohall integrations\` and follow the entry for the
|
|
4264
|
+
actual harness in [agent integrations](https://github.com/AksharP5/cohall/blob/main/docs/integrations.md). Configure exactly
|
|
4265
|
+
one Cohall server for that configuration, restart or reconnect the harness,
|
|
4266
|
+
and ask it to call \`list_devices\`. Installing a skill does not configure MCP.
|
|
4267
|
+
|
|
4268
|
+
## Verify before calling setup complete
|
|
4269
|
+
|
|
4270
|
+
1. Run \`cohall doctor\`. Confirm relay access and client authentication. A worker
|
|
4271
|
+
should be online or busy with the intended providers and workspaces.
|
|
4272
|
+
\`doctor --all\` checks the other registered workers.
|
|
4273
|
+
2. If using MCP, check the real host connection in the doctor report after
|
|
4274
|
+
asking the harness to call \`list_devices\`. A successful server self-test
|
|
4275
|
+
alone does not establish that the harness loaded it.
|
|
4276
|
+
3. From the requesting client, run a harmless delegation to the intended
|
|
4277
|
+
worker, using its target from \`cohall devices\` and an advertised provider:
|
|
4278
|
+
|
|
4279
|
+
\`\`\`bash
|
|
4280
|
+
cohall delegate --target @workstation --provider codex --timeout 60 \\
|
|
4281
|
+
"Reply with exactly cohall-ready. Do not inspect or change files."
|
|
4282
|
+
\`\`\`
|
|
4283
|
+
|
|
4284
|
+
Require a completed result containing \`cohall-ready\`. Provider detection
|
|
4285
|
+
alone does not prove authentication or execution works. For a Grok Bot,
|
|
4286
|
+
use its target from \`cohall bots\` and \`--provider grok-bot\`.
|
|
4287
|
+
|
|
4288
|
+
4. Report the relay host/address, this machine's role, selected providers and
|
|
4289
|
+
workspaces, autostart state, integration used, and checks that passed.
|
|
4290
|
+
Identify any remaining login, permission, or connectivity step. Include no
|
|
4291
|
+
tokens or private task content.
|
|
4292
|
+
`;
|
|
4293
|
+
|
|
4175
4294
|
// apps/device/src/config.ts
|
|
4176
4295
|
init_src();
|
|
4177
4296
|
import { Effect as Effect4, Schema as Schema4 } from "effect";
|
|
@@ -5239,9 +5358,15 @@ var terminalPrompter = () => {
|
|
|
5239
5358
|
close: () => terminal.close()
|
|
5240
5359
|
};
|
|
5241
5360
|
};
|
|
5361
|
+
var setupRelayUrl = (input) => {
|
|
5362
|
+
if (input === undefined || input.trim().length === 0) {
|
|
5363
|
+
throw new Error("Relay URL is required. Run `cohall onboard` to choose a relay host or join an existing relay.");
|
|
5364
|
+
}
|
|
5365
|
+
return normalizeRelayUrl(input);
|
|
5366
|
+
};
|
|
5242
5367
|
var guidedSetupInput = async (options, prompter) => {
|
|
5243
5368
|
const existing = await readStoredConfiguration();
|
|
5244
|
-
const relayUrl =
|
|
5369
|
+
const relayUrl = setupRelayUrl(options.relayUrl ?? await prompter.answer("Relay URL", existing?.relayUrl ?? ""));
|
|
5245
5370
|
const credentials = credentialsForRelay(existing, relayUrl);
|
|
5246
5371
|
const hasRequiredCredentials = credentials.clientToken !== undefined && (options.clientOnly || credentials.deviceToken !== undefined);
|
|
5247
5372
|
let workspaces = options.clientOnly ? existing?.workspaces ?? [] : options.workspaces;
|
|
@@ -6722,6 +6847,248 @@ import {
|
|
|
6722
6847
|
getDefaultEnvironment,
|
|
6723
6848
|
StdioClientTransport
|
|
6724
6849
|
} from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6850
|
+
|
|
6851
|
+
// apps/device/src/mcp-host-diagnostics.ts
|
|
6852
|
+
init_src();
|
|
6853
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6854
|
+
import { Schema as Schema11 } from "effect";
|
|
6855
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6856
|
+
import { constants as constants4 } from "node:fs";
|
|
6857
|
+
import { lstat as lstat3, mkdir as mkdir7, open as open4, opendir, rename as rename4, rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
6858
|
+
import { join as join8 } from "node:path";
|
|
6859
|
+
var maxRecords = 8;
|
|
6860
|
+
var maxRecordBytes = 4096;
|
|
6861
|
+
var retentionMs = 7 * 24 * 60 * 60 * 1000;
|
|
6862
|
+
var recordName = /^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}\.json$/;
|
|
6863
|
+
var text3 = Schema11.String.check(Schema11.isMaxLength(128));
|
|
6864
|
+
var count2 = Schema11.Int.check(Schema11.isBetween({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER }));
|
|
6865
|
+
var McpHostSession = Schema11.Struct({
|
|
6866
|
+
schema_version: Schema11.Literal(1),
|
|
6867
|
+
launch_id: Schema11.String.check(Schema11.isUUID(4)),
|
|
6868
|
+
launched_at: Timestamp,
|
|
6869
|
+
server_version: text3,
|
|
6870
|
+
initialized_at: Schema11.optionalKey(Timestamp),
|
|
6871
|
+
client: Schema11.optionalKey(Schema11.Struct({ name: text3, version: text3 })),
|
|
6872
|
+
tools_list: Schema11.optionalKey(Schema11.Struct({ at: Timestamp, tool_count: count2 })),
|
|
6873
|
+
tools_call_count: count2,
|
|
6874
|
+
last_tool_call_at: Schema11.optionalKey(Timestamp),
|
|
6875
|
+
closed_at: Schema11.optionalKey(Timestamp)
|
|
6876
|
+
});
|
|
6877
|
+
var storagePath = (configPath) => `${configPath}.mcp-hosts`;
|
|
6878
|
+
var recordsIn = async (directory) => {
|
|
6879
|
+
const records = [];
|
|
6880
|
+
let entries = 0;
|
|
6881
|
+
for await (const entry of await opendir(directory)) {
|
|
6882
|
+
if (++entries > 64)
|
|
6883
|
+
throw new Error("MCP host diagnostics directory exceeded its entry limit");
|
|
6884
|
+
if (!recordName.test(entry.name))
|
|
6885
|
+
continue;
|
|
6886
|
+
const path = join8(directory, entry.name);
|
|
6887
|
+
const metadata = await lstat3(path).catch((cause) => {
|
|
6888
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
|
|
6889
|
+
return;
|
|
6890
|
+
throw cause;
|
|
6891
|
+
});
|
|
6892
|
+
if (metadata !== undefined)
|
|
6893
|
+
records.push({ path, modified: metadata.mtimeMs });
|
|
6894
|
+
}
|
|
6895
|
+
return records.sort((left, right) => right.modified - left.modified || left.path.localeCompare(right.path));
|
|
6896
|
+
};
|
|
6897
|
+
var readMcpHostDiagnostics = async (configPath = configurationPath()) => {
|
|
6898
|
+
const path = storagePath(configPath);
|
|
6899
|
+
const warnings = [];
|
|
6900
|
+
let records;
|
|
6901
|
+
try {
|
|
6902
|
+
records = await recordsIn(path);
|
|
6903
|
+
} catch (cause) {
|
|
6904
|
+
if (cause instanceof Error && "code" in cause && cause.code === "ENOENT") {
|
|
6905
|
+
return { status: "not_observed", path, sessions: [], warnings };
|
|
6906
|
+
}
|
|
6907
|
+
return {
|
|
6908
|
+
status: "unavailable",
|
|
6909
|
+
path,
|
|
6910
|
+
sessions: [],
|
|
6911
|
+
warnings,
|
|
6912
|
+
error: `Cannot read MCP host diagnostics at ${path}. Restore access to this diagnostics directory, then restart the Cohall MCP connection in your host.`
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6915
|
+
const sessions = [];
|
|
6916
|
+
let invalid = 0;
|
|
6917
|
+
for (const record2 of records.filter((record3) => record3.modified >= Date.now() - retentionMs).slice(0, maxRecords)) {
|
|
6918
|
+
try {
|
|
6919
|
+
const handle = await open4(record2.path, constants4.O_RDONLY | (process.platform === "win32" ? 0 : constants4.O_NOFOLLOW | constants4.O_NONBLOCK));
|
|
6920
|
+
try {
|
|
6921
|
+
const metadata = await handle.stat();
|
|
6922
|
+
if (!metadata.isFile() || metadata.size > maxRecordBytes)
|
|
6923
|
+
throw new Error("Invalid record");
|
|
6924
|
+
const data = await readBoundedFile(handle, maxRecordBytes);
|
|
6925
|
+
if (data.length > maxRecordBytes)
|
|
6926
|
+
throw new Error("Invalid record");
|
|
6927
|
+
sessions.push(Schema11.decodeUnknownSync(McpHostSession)(JSON.parse(data.toString("utf8"))));
|
|
6928
|
+
} finally {
|
|
6929
|
+
await handle.close();
|
|
6930
|
+
}
|
|
6931
|
+
} catch (cause) {
|
|
6932
|
+
if (!(cause instanceof Error && ("code" in cause) && cause.code === "ENOENT"))
|
|
6933
|
+
invalid += 1;
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6936
|
+
if (invalid > 0) {
|
|
6937
|
+
warnings.push(`${invalid} MCP host diagnostics record(s) could not be read. Reset the diagnostics directory ${path}, then restart the Cohall MCP connection in your host.`);
|
|
6938
|
+
}
|
|
6939
|
+
if (sessions.length === 0 && invalid > 0) {
|
|
6940
|
+
return {
|
|
6941
|
+
status: "unavailable",
|
|
6942
|
+
path,
|
|
6943
|
+
sessions,
|
|
6944
|
+
warnings,
|
|
6945
|
+
error: "No valid MCP host diagnostics records could be read"
|
|
6946
|
+
};
|
|
6947
|
+
}
|
|
6948
|
+
const latest = sessions[0];
|
|
6949
|
+
if (latest !== undefined) {
|
|
6950
|
+
if (latest.initialized_at === undefined) {
|
|
6951
|
+
warnings.push(`${latest.closed_at === undefined ? "An MCP launch was recorded" : "A recorded MCP launch ended"} without client initialization. If your host still lacks Cohall tools, check its MCP command and selected config, then restart its Cohall connection.`);
|
|
6952
|
+
} else if (latest.tools_list?.tool_count === 0) {
|
|
6953
|
+
warnings.push("A recorded MCP launch returned an empty tools list. If Cohall tools are missing, check the host's Cohall MCP command and update or restart its connection.");
|
|
6954
|
+
} else if (latest.tools_list === undefined && latest.tools_call_count === 0) {
|
|
6955
|
+
warnings.push("An MCP host initialized Cohall but no tool discovery or calls were observed. If Cohall tools are missing, refresh or restart its MCP connection in that host.");
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
return {
|
|
6959
|
+
status: sessions.some((session) => session.initialized_at !== undefined) ? "observed" : "not_observed",
|
|
6960
|
+
path,
|
|
6961
|
+
sessions,
|
|
6962
|
+
warnings
|
|
6963
|
+
};
|
|
6964
|
+
};
|
|
6965
|
+
var createMcpHostDiagnostics = (server) => {
|
|
6966
|
+
const transport = new StdioServerTransport;
|
|
6967
|
+
if (process.env.COHALL_MCP_SELF_TEST === "1")
|
|
6968
|
+
return transport;
|
|
6969
|
+
const directory = storagePath(configurationPath());
|
|
6970
|
+
let session = McpHostSession.make({
|
|
6971
|
+
schema_version: 1,
|
|
6972
|
+
launch_id: randomUUID2(),
|
|
6973
|
+
launched_at: now(),
|
|
6974
|
+
server_version: version,
|
|
6975
|
+
tools_call_count: 0
|
|
6976
|
+
});
|
|
6977
|
+
const path = join8(directory, `${session.launch_id}.json`);
|
|
6978
|
+
let dirty = true;
|
|
6979
|
+
let disabled = false;
|
|
6980
|
+
let stopped = false;
|
|
6981
|
+
let writing;
|
|
6982
|
+
let timer;
|
|
6983
|
+
const schedule = () => {
|
|
6984
|
+
if (disabled || stopped || timer !== undefined)
|
|
6985
|
+
return;
|
|
6986
|
+
timer = setTimeout(() => {
|
|
6987
|
+
timer = undefined;
|
|
6988
|
+
flush();
|
|
6989
|
+
}, 250);
|
|
6990
|
+
timer.unref();
|
|
6991
|
+
};
|
|
6992
|
+
const flush = () => {
|
|
6993
|
+
if (writing !== undefined)
|
|
6994
|
+
return writing;
|
|
6995
|
+
if (!dirty || disabled)
|
|
6996
|
+
return Promise.resolve();
|
|
6997
|
+
dirty = false;
|
|
6998
|
+
const snapshot = session;
|
|
6999
|
+
writing = (async () => {
|
|
7000
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
7001
|
+
await recordsIn(directory);
|
|
7002
|
+
const temporary = `${path}.tmp`;
|
|
7003
|
+
try {
|
|
7004
|
+
const json = `${JSON.stringify(snapshot)}
|
|
7005
|
+
`;
|
|
7006
|
+
if (Buffer.byteLength(json) > maxRecordBytes)
|
|
7007
|
+
throw new Error("Record exceeded its limit");
|
|
7008
|
+
await writeFile7(temporary, json, { flag: "wx", mode: 384 });
|
|
7009
|
+
await rename4(temporary, path);
|
|
7010
|
+
} finally {
|
|
7011
|
+
await rm5(temporary, { force: true });
|
|
7012
|
+
}
|
|
7013
|
+
const records = await recordsIn(directory);
|
|
7014
|
+
const expired = records.filter((record2, index) => index >= maxRecords || record2.modified < Date.now() - retentionMs);
|
|
7015
|
+
await Promise.all(expired.map((record2) => rm5(record2.path, { force: true })));
|
|
7016
|
+
})().catch((cause) => {
|
|
7017
|
+
disabled = true;
|
|
7018
|
+
const reason = cause instanceof Error ? "code" in cause && typeof cause.code === "string" ? cause.code : cause.message : "Unknown filesystem error";
|
|
7019
|
+
console.error(`Cohall MCP host diagnostics are unavailable (${reason.replace(/[\r\n]/g, " ").slice(0, 160)}). Check diagnostics storage access; MCP tools remain available.`);
|
|
7020
|
+
}).finally(() => {
|
|
7021
|
+
writing = undefined;
|
|
7022
|
+
if (dirty)
|
|
7023
|
+
schedule();
|
|
7024
|
+
});
|
|
7025
|
+
return writing;
|
|
7026
|
+
};
|
|
7027
|
+
const changed = () => {
|
|
7028
|
+
dirty = true;
|
|
7029
|
+
schedule();
|
|
7030
|
+
};
|
|
7031
|
+
const shutdown = async () => {
|
|
7032
|
+
if (stopped)
|
|
7033
|
+
return;
|
|
7034
|
+
stopped = true;
|
|
7035
|
+
clearTimeout(timer);
|
|
7036
|
+
session = { ...session, closed_at: now() };
|
|
7037
|
+
dirty = true;
|
|
7038
|
+
await writing;
|
|
7039
|
+
await flush();
|
|
7040
|
+
};
|
|
7041
|
+
flush();
|
|
7042
|
+
process.once("beforeExit", () => void shutdown());
|
|
7043
|
+
const initialized = server.oninitialized;
|
|
7044
|
+
server.oninitialized = () => {
|
|
7045
|
+
initialized?.();
|
|
7046
|
+
const client = server.getClientVersion();
|
|
7047
|
+
session = {
|
|
7048
|
+
...session,
|
|
7049
|
+
initialized_at: now(),
|
|
7050
|
+
...client === undefined ? {} : { client: { name: client.name.slice(0, 128), version: client.version.slice(0, 128) } }
|
|
7051
|
+
};
|
|
7052
|
+
changed();
|
|
7053
|
+
};
|
|
7054
|
+
const closed = server.onclose;
|
|
7055
|
+
server.onclose = () => {
|
|
7056
|
+
closed?.();
|
|
7057
|
+
shutdown();
|
|
7058
|
+
};
|
|
7059
|
+
const pendingLists = new Set;
|
|
7060
|
+
const start = transport.start.bind(transport);
|
|
7061
|
+
transport.start = async () => {
|
|
7062
|
+
const receive = transport.onmessage;
|
|
7063
|
+
transport.onmessage = (message) => {
|
|
7064
|
+
if ("id" in message && message.id !== undefined && "method" in message) {
|
|
7065
|
+
if (message.method === "tools/list" && pendingLists.size < 32)
|
|
7066
|
+
pendingLists.add(message.id);
|
|
7067
|
+
if (message.method === "tools/call") {
|
|
7068
|
+
session = {
|
|
7069
|
+
...session,
|
|
7070
|
+
tools_call_count: Math.min(session.tools_call_count + 1, Number.MAX_SAFE_INTEGER),
|
|
7071
|
+
last_tool_call_at: now()
|
|
7072
|
+
};
|
|
7073
|
+
changed();
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
receive?.(message);
|
|
7077
|
+
};
|
|
7078
|
+
await start();
|
|
7079
|
+
};
|
|
7080
|
+
const send = transport.send.bind(transport);
|
|
7081
|
+
transport.send = async (message) => {
|
|
7082
|
+
await send(message);
|
|
7083
|
+
if ("id" in message && message.id !== undefined && pendingLists.delete(message.id) && "result" in message && Array.isArray(message.result.tools)) {
|
|
7084
|
+
session = { ...session, tools_list: { at: now(), tool_count: message.result.tools.length } };
|
|
7085
|
+
changed();
|
|
7086
|
+
}
|
|
7087
|
+
};
|
|
7088
|
+
return transport;
|
|
7089
|
+
};
|
|
7090
|
+
|
|
7091
|
+
// apps/device/src/mcp-diagnostics.ts
|
|
6725
7092
|
var checkMcp = async (entrypoint, relayUrl, token) => {
|
|
6726
7093
|
const client = new Client({ name: "cohall-doctor", version });
|
|
6727
7094
|
const transport = new StdioClientTransport({
|
|
@@ -6730,6 +7097,7 @@ var checkMcp = async (entrypoint, relayUrl, token) => {
|
|
|
6730
7097
|
env: {
|
|
6731
7098
|
...getDefaultEnvironment(),
|
|
6732
7099
|
COHALL_CONFIG: configurationPath(),
|
|
7100
|
+
COHALL_MCP_SELF_TEST: "1",
|
|
6733
7101
|
COHALL_RELAY_URL: relayUrl,
|
|
6734
7102
|
COHALL_CLIENT_TOKEN: token
|
|
6735
7103
|
},
|
|
@@ -6800,6 +7168,7 @@ var aliases = new Map([
|
|
|
6800
7168
|
var help = `Cohall connects agents running on your own devices.
|
|
6801
7169
|
|
|
6802
7170
|
Usage:
|
|
7171
|
+
cohall onboard
|
|
6803
7172
|
cohall init [--relay url] [--workspace path] [--providers list] [--service]
|
|
6804
7173
|
cohall join --relay <url> --workspace <path> [--providers list] [--token-file <path>]
|
|
6805
7174
|
cohall configure [--relay url] [--name name] [--workspace path]
|
|
@@ -6930,7 +7299,7 @@ var printLine = (value) => console.log(JSON.stringify(value));
|
|
|
6930
7299
|
var readTextFile = async (path, limit, exceeded) => {
|
|
6931
7300
|
if ((await stat4(path)).size > limit)
|
|
6932
7301
|
throw new Error(exceeded);
|
|
6933
|
-
const file = await
|
|
7302
|
+
const file = await open5(path, "r");
|
|
6934
7303
|
try {
|
|
6935
7304
|
const data = await readBoundedFile(file, limit);
|
|
6936
7305
|
if (data.length > limit)
|
|
@@ -7005,17 +7374,17 @@ var ownerClient = async () => {
|
|
|
7005
7374
|
};
|
|
7006
7375
|
var installSkill = async (target) => {
|
|
7007
7376
|
const roots = {
|
|
7008
|
-
agents:
|
|
7009
|
-
claude:
|
|
7010
|
-
opencode:
|
|
7377
|
+
agents: join9(homedir4(), ".agents", "skills", "cohall", "SKILL.md"),
|
|
7378
|
+
claude: join9(homedir4(), ".claude", "skills", "cohall", "SKILL.md"),
|
|
7379
|
+
opencode: join9(homedir4(), ".config", "opencode", "skills", "cohall", "SKILL.md")
|
|
7011
7380
|
};
|
|
7012
7381
|
const selected = target === "all" ? Object.values(roots) : (target in roots) ? [roots[target]] : undefined;
|
|
7013
7382
|
if (selected === undefined) {
|
|
7014
7383
|
throw new Error("Skill target must be agents, claude, opencode, or all");
|
|
7015
7384
|
}
|
|
7016
7385
|
for (const path of selected) {
|
|
7017
|
-
await
|
|
7018
|
-
await
|
|
7386
|
+
await mkdir8(dirname6(path), { recursive: true });
|
|
7387
|
+
await writeFile8(path, SKILL_default);
|
|
7019
7388
|
}
|
|
7020
7389
|
return selected;
|
|
7021
7390
|
};
|
|
@@ -7023,13 +7392,19 @@ var printHelp = () => console.log(help);
|
|
|
7023
7392
|
var printSkill = () => console.log(SKILL_default.trimEnd());
|
|
7024
7393
|
var runCli = async (command, raw) => {
|
|
7025
7394
|
const arguments_ = parseArguments(raw);
|
|
7395
|
+
if (command === "onboard") {
|
|
7396
|
+
allowOptions(arguments_, []);
|
|
7397
|
+
noPositionals(arguments_, command);
|
|
7398
|
+
console.log(onboarding_default.trimEnd());
|
|
7399
|
+
return;
|
|
7400
|
+
}
|
|
7026
7401
|
if (command === "reply") {
|
|
7027
7402
|
allowOptions(arguments_, ["message", "message-file", "error", "question", "run-id"]);
|
|
7028
|
-
const id =
|
|
7403
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7029
7404
|
const error = option(arguments_, "error");
|
|
7030
7405
|
const question = option(arguments_, "question");
|
|
7031
7406
|
const rawRunId = option(arguments_, "run-id");
|
|
7032
|
-
const runId = rawRunId === undefined ? undefined :
|
|
7407
|
+
const runId = rawRunId === undefined ? undefined : Schema12.decodeUnknownSync(TaskRunId)(rawRunId);
|
|
7033
7408
|
if ((error !== undefined || question !== undefined) && (option(arguments_, "message") !== undefined || option(arguments_, "message-file") !== undefined || error !== undefined && question !== undefined)) {
|
|
7034
7409
|
throw new Error("Use one of --question, --error, or --message/--message-file");
|
|
7035
7410
|
}
|
|
@@ -7037,7 +7412,7 @@ var runCli = async (command, raw) => {
|
|
|
7037
7412
|
if (error === undefined && question === undefined && message === undefined) {
|
|
7038
7413
|
throw new Error("A bot reply requires --message, --message-file, --error, or --question");
|
|
7039
7414
|
}
|
|
7040
|
-
const reply = question !== undefined ? { question:
|
|
7415
|
+
const reply = question !== undefined ? { question: Schema12.decodeUnknownSync(RequestTaskInput.fields.question)(question) } : error === undefined ? { result: message ?? "" } : { error };
|
|
7041
7416
|
await writeBotReply(id, reply, runId);
|
|
7042
7417
|
print({ submitted: true, task_id: id });
|
|
7043
7418
|
return;
|
|
@@ -7053,6 +7428,9 @@ var runCli = async (command, raw) => {
|
|
|
7053
7428
|
"workspace"
|
|
7054
7429
|
]);
|
|
7055
7430
|
noPositionals(arguments_, command);
|
|
7431
|
+
if (arguments_.options.has("client-only") && arguments_.options.has("service")) {
|
|
7432
|
+
throw new Error("A client-only setup cannot install a device service");
|
|
7433
|
+
}
|
|
7056
7434
|
const token = option(arguments_, "token-file") === undefined ? undefined : await pairingToken(arguments_);
|
|
7057
7435
|
const interactive = process.stdin.isTTY === true && process.stderr.isTTY === true;
|
|
7058
7436
|
const relayUrl = option(arguments_, "relay");
|
|
@@ -7111,7 +7489,7 @@ var runCli = async (command, raw) => {
|
|
|
7111
7489
|
workspaces: result.configuration.workspaces,
|
|
7112
7490
|
skills: installedSkills,
|
|
7113
7491
|
...service === undefined ? {} : { service },
|
|
7114
|
-
next: service === undefined && !arguments_.options.has("client-only") ? "Run `cohall service install` for background availability, or `cohall device` now." : "Run `cohall doctor
|
|
7492
|
+
next: service === undefined && !arguments_.options.has("client-only") ? "Run `cohall service install` for background availability, or `cohall device` now. Then run `cohall doctor` and the delegation check in `cohall onboard`." : "Run `cohall doctor`, then the delegation check in `cohall onboard`."
|
|
7115
7493
|
});
|
|
7116
7494
|
} finally {
|
|
7117
7495
|
prompter.close();
|
|
@@ -7203,7 +7581,7 @@ var runCli = async (command, raw) => {
|
|
|
7203
7581
|
if (arguments_.positionals.length !== 2 || arguments_.positionals[0] !== "abandon") {
|
|
7204
7582
|
throw new Error("Usage: cohall upgrades [abandon <operation-id>]");
|
|
7205
7583
|
}
|
|
7206
|
-
print(await Effect10.runPromise(relay2.abandonOperation(
|
|
7584
|
+
print(await Effect10.runPromise(relay2.abandonOperation(Schema12.decodeUnknownSync(OperationId)(arguments_.positionals[1]))));
|
|
7207
7585
|
return;
|
|
7208
7586
|
}
|
|
7209
7587
|
print(await Effect10.runPromise(relay2.operations()));
|
|
@@ -7219,9 +7597,9 @@ var runCli = async (command, raw) => {
|
|
|
7219
7597
|
"workspace"
|
|
7220
7598
|
]);
|
|
7221
7599
|
noPositionals(arguments_, command);
|
|
7222
|
-
const token = await pairingToken(arguments_);
|
|
7223
|
-
const relayUrl = normalizeRelayUrl(option(arguments_, "relay") ?? "http://127.0.0.1:8787");
|
|
7224
7600
|
const existing = await readStoredConfiguration();
|
|
7601
|
+
const relayUrl = setupRelayUrl(option(arguments_, "relay") ?? existing?.relayUrl);
|
|
7602
|
+
const token = await pairingToken(arguments_);
|
|
7225
7603
|
const suppliedWorkspaces = values(arguments_, "workspace");
|
|
7226
7604
|
const workspaces = suppliedWorkspaces.length === 0 ? existing?.workspaces : suppliedWorkspaces;
|
|
7227
7605
|
const deviceName = option(arguments_, "name");
|
|
@@ -7303,7 +7681,7 @@ var runCli = async (command, raw) => {
|
|
|
7303
7681
|
...model === undefined ? {} : { model },
|
|
7304
7682
|
...grokGateway === undefined ? {} : { grokGateway },
|
|
7305
7683
|
...(sandbox ?? existing?.sandbox) === undefined ? {} : {
|
|
7306
|
-
sandbox:
|
|
7684
|
+
sandbox: Schema12.decodeUnknownSync(Schema12.Literals(["read-only", "workspace-write", "danger-full-access"]))(sandbox ?? existing?.sandbox)
|
|
7307
7685
|
}
|
|
7308
7686
|
});
|
|
7309
7687
|
await writeStoredConfiguration(configuration2);
|
|
@@ -7368,6 +7746,7 @@ var runCli = async (command, raw) => {
|
|
|
7368
7746
|
return;
|
|
7369
7747
|
});
|
|
7370
7748
|
const clientToken = process.env.COHALL_CLIENT_TOKEN ?? storedCredentials.clientToken;
|
|
7749
|
+
const mcpHost = await readMcpHostDiagnostics();
|
|
7371
7750
|
const mcp = clientToken === undefined ? { status: "not configured" } : await checkMcp(process.argv[1] ?? "", relayUrl, clientToken);
|
|
7372
7751
|
const hasDeviceCredential = process.env.COHALL_DEVICE_TOKEN !== undefined || storedCredentials.deviceToken !== undefined;
|
|
7373
7752
|
const deviceId = process.env.COHALL_DEVICE_ID ?? configuration2?.deviceId;
|
|
@@ -7389,6 +7768,8 @@ var runCli = async (command, raw) => {
|
|
|
7389
7768
|
...response?.ok === true ? [] : ["Relay is unreachable"],
|
|
7390
7769
|
...versionWarning === undefined ? [] : [versionWarning],
|
|
7391
7770
|
...mcp.status === "error" ? [`MCP check failed: ${mcp.error}`] : [],
|
|
7771
|
+
...mcpHost.warnings,
|
|
7772
|
+
...mcpHost.status === "unavailable" ? [`MCP host diagnostics unavailable: ${mcpHost.error}`] : [],
|
|
7392
7773
|
...clientAuthentication.status === "error" ? [clientAuthentication.error] : [],
|
|
7393
7774
|
...hasDeviceCredential && response?.ok === true && clientAuthentication.status !== "error" && currentDevice?.status !== "online" && currentDevice?.status !== "busy" ? [
|
|
7394
7775
|
clientToken === undefined ? "Device status cannot be checked without a client credential" : "Device daemon is not connected or is not registered with the relay"
|
|
@@ -7404,7 +7785,8 @@ var runCli = async (command, raw) => {
|
|
|
7404
7785
|
config_path: configurationPath(),
|
|
7405
7786
|
client_credential: process.env.COHALL_CLIENT_TOKEN !== undefined || storedCredentials.clientToken !== undefined,
|
|
7406
7787
|
client_authentication: clientAuthentication.status === "ok" ? { status: "ok" } : clientAuthentication,
|
|
7407
|
-
mcp,
|
|
7788
|
+
mcp: { ...mcp, scope: "server self-test" },
|
|
7789
|
+
mcp_host: mcpHost,
|
|
7408
7790
|
device_credential: hasDeviceCredential,
|
|
7409
7791
|
workspaces: configuration2?.workspaces ?? [],
|
|
7410
7792
|
providers: providerExecutables,
|
|
@@ -7451,7 +7833,8 @@ var runCli = async (command, raw) => {
|
|
|
7451
7833
|
relay_url: configuration2.relayUrl,
|
|
7452
7834
|
pairing_token: credential.token,
|
|
7453
7835
|
expires_at: credential.expiresAt,
|
|
7454
|
-
roles
|
|
7836
|
+
roles,
|
|
7837
|
+
join_instructions: `Join my existing Cohall relay at ${configuration2.relayUrl} as ${arguments_.options.has("client-only") ? "a client that submits work only" : "a worker and client"}. Follow https://github.com/AksharP5/cohall/blob/main/docs/onboarding.md, "Join an existing relay". I will provide the one-time pairing token separately; read it through a hidden terminal prompt, stdin, or a token file. ${arguments_.options.has("client-only") ? "Use cohall init --client-only; no device service is needed." : "Choose existing workspace roots and installed providers before running cohall init."} Finish the verification steps and report what passed.`
|
|
7455
7838
|
});
|
|
7456
7839
|
return;
|
|
7457
7840
|
}
|
|
@@ -7463,11 +7846,11 @@ var runCli = async (command, raw) => {
|
|
|
7463
7846
|
}
|
|
7464
7847
|
if (command === "forget") {
|
|
7465
7848
|
allowOptions(arguments_, []);
|
|
7466
|
-
print(await Effect10.runPromise(relay2.forgetDevice(
|
|
7849
|
+
print(await Effect10.runPromise(relay2.forgetDevice(Schema12.decodeUnknownSync(DeviceId)(identifier(arguments_, "device id")))));
|
|
7467
7850
|
return;
|
|
7468
7851
|
}
|
|
7469
7852
|
allowOptions(arguments_, []);
|
|
7470
|
-
print(await Effect10.runPromise(relay2.revokeAuthSession(
|
|
7853
|
+
print(await Effect10.runPromise(relay2.revokeAuthSession(Schema12.decodeUnknownSync(AuthSessionId)(identifier(arguments_, "session id")))));
|
|
7471
7854
|
return;
|
|
7472
7855
|
}
|
|
7473
7856
|
const { configuration, relay } = await client();
|
|
@@ -7476,20 +7859,20 @@ var runCli = async (command, raw) => {
|
|
|
7476
7859
|
const rawQuestion = option(arguments_, "question");
|
|
7477
7860
|
if (rawQuestion === undefined)
|
|
7478
7861
|
throw new Error("--question is required");
|
|
7479
|
-
const question =
|
|
7480
|
-
const id = arguments_.positionals.length === 0 ? undefined :
|
|
7862
|
+
const question = Schema12.decodeUnknownSync(RequestTaskInput.fields.question)(rawQuestion);
|
|
7863
|
+
const id = arguments_.positionals.length === 0 ? undefined : Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7481
7864
|
const rawRunId = option(arguments_, "run-id");
|
|
7482
|
-
const runId = rawRunId === undefined ? undefined :
|
|
7865
|
+
const runId = rawRunId === undefined ? undefined : Schema12.decodeUnknownSync(TaskRunId)(rawRunId);
|
|
7483
7866
|
print(await Effect10.runPromise(requestInput(relay, configuration, question, id, runId)));
|
|
7484
7867
|
return;
|
|
7485
7868
|
}
|
|
7486
7869
|
if (command === "answer") {
|
|
7487
7870
|
allowOptions(arguments_, ["request-id", "message", "message-file"]);
|
|
7488
|
-
const id =
|
|
7871
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7489
7872
|
const requestId = option(arguments_, "request-id");
|
|
7490
7873
|
if (requestId === undefined)
|
|
7491
7874
|
throw new Error("--request-id is required; use the input request id from status or inbox");
|
|
7492
|
-
const input =
|
|
7875
|
+
const input = Schema12.decodeUnknownSync(AnswerTaskInput)({
|
|
7493
7876
|
requestId,
|
|
7494
7877
|
answer: await readInput(arguments_, "message")
|
|
7495
7878
|
});
|
|
@@ -7504,8 +7887,8 @@ var runCli = async (command, raw) => {
|
|
|
7504
7887
|
const note = option(arguments_, "message");
|
|
7505
7888
|
if (note === undefined)
|
|
7506
7889
|
throw new Error("--message is required");
|
|
7507
|
-
const input =
|
|
7508
|
-
print(await Effect10.runPromise(relay.reportTaskProgress(
|
|
7890
|
+
const input = Schema12.decodeUnknownSync(TaskProgressInput)({ note });
|
|
7891
|
+
print(await Effect10.runPromise(relay.reportTaskProgress(Schema12.decodeUnknownSync(TaskId)(rawId), input)));
|
|
7509
7892
|
return;
|
|
7510
7893
|
}
|
|
7511
7894
|
if (command === "devices") {
|
|
@@ -7565,15 +7948,15 @@ var runCli = async (command, raw) => {
|
|
|
7565
7948
|
const requestId = option(arguments_, "request-id");
|
|
7566
7949
|
const task = await Effect10.runPromise(createDelegation(relay, configuration, {
|
|
7567
7950
|
prompt,
|
|
7568
|
-
...requestId === undefined ? {} : { requestId:
|
|
7951
|
+
...requestId === undefined ? {} : { requestId: Schema12.decodeUnknownSync(TaskRequestId)(requestId) },
|
|
7569
7952
|
...target === undefined ? {} : { target },
|
|
7570
7953
|
...context === undefined ? {} : { context },
|
|
7571
|
-
...thread === undefined ? {} : { threadId:
|
|
7572
|
-
...parent === undefined ? {} : { parentTaskId:
|
|
7954
|
+
...thread === undefined ? {} : { threadId: Schema12.decodeUnknownSync(ThreadId)(thread) },
|
|
7955
|
+
...parent === undefined ? {} : { parentTaskId: Schema12.decodeUnknownSync(TaskId)(parent) },
|
|
7573
7956
|
...workspace === undefined ? {} : { workspace },
|
|
7574
|
-
...provider === undefined ? {} : { provider:
|
|
7957
|
+
...provider === undefined ? {} : { provider: Schema12.decodeUnknownSync(Provider)(provider) },
|
|
7575
7958
|
...attachments2.length === 0 ? {} : { attachments: attachments2 },
|
|
7576
|
-
...deadline === undefined ? {} : { expiresAt:
|
|
7959
|
+
...deadline === undefined ? {} : { expiresAt: Schema12.decodeUnknownSync(TaskDeadline)(deadline) }
|
|
7577
7960
|
}));
|
|
7578
7961
|
const completed = arguments_.options.has("no-wait") ? task : await Effect10.runPromise(waitForTask(relay, task, waitSeconds));
|
|
7579
7962
|
print(arguments_.options.has("no-wait") ? taskResult(completed) : await acknowledgedTaskResult(relay, completed));
|
|
@@ -7584,7 +7967,7 @@ var runCli = async (command, raw) => {
|
|
|
7584
7967
|
if (arguments_.positionals[0] === "ack") {
|
|
7585
7968
|
if (arguments_.positionals.length !== 2)
|
|
7586
7969
|
throw new Error("Usage: cohall inbox ack <task-id>");
|
|
7587
|
-
const id =
|
|
7970
|
+
const id = Schema12.decodeUnknownSync(TaskId)(arguments_.positionals[1]);
|
|
7588
7971
|
print(await Effect10.runPromise(relay.acknowledgeCompletion(id)));
|
|
7589
7972
|
return;
|
|
7590
7973
|
}
|
|
@@ -7594,13 +7977,13 @@ var runCli = async (command, raw) => {
|
|
|
7594
7977
|
}
|
|
7595
7978
|
if (command === "status") {
|
|
7596
7979
|
allowOptions(arguments_, []);
|
|
7597
|
-
const id =
|
|
7980
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7598
7981
|
print(taskResult(await Effect10.runPromise(relay.getTask(id))));
|
|
7599
7982
|
return;
|
|
7600
7983
|
}
|
|
7601
7984
|
if (command === "attachments") {
|
|
7602
7985
|
allowOptions(arguments_, []);
|
|
7603
|
-
const id =
|
|
7986
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7604
7987
|
print(await Effect10.runPromise(relay.listAttachments(id)));
|
|
7605
7988
|
return;
|
|
7606
7989
|
}
|
|
@@ -7613,18 +7996,18 @@ var runCli = async (command, raw) => {
|
|
|
7613
7996
|
if (destination === undefined) {
|
|
7614
7997
|
throw new Error("--output is required");
|
|
7615
7998
|
}
|
|
7616
|
-
const id =
|
|
7617
|
-
const name =
|
|
7999
|
+
const id = Schema12.decodeUnknownSync(TaskId)(arguments_.positionals[0]);
|
|
8000
|
+
const name = Schema12.decodeUnknownSync(AttachmentName)(arguments_.positionals[1]);
|
|
7618
8001
|
const directionInput = option(arguments_, "direction");
|
|
7619
|
-
const direction = directionInput === undefined ? undefined :
|
|
8002
|
+
const direction = directionInput === undefined ? undefined : Schema12.decodeUnknownSync(AttachmentDirection)(directionInput);
|
|
7620
8003
|
const data = await Effect10.runPromise(relay.readAttachment(id, name, direction));
|
|
7621
|
-
await
|
|
8004
|
+
await writeFile8(destination, data, { flag: "wx", mode: 384 });
|
|
7622
8005
|
print({ task_id: id, name, path: resolve4(destination), bytes: data.length });
|
|
7623
8006
|
return;
|
|
7624
8007
|
}
|
|
7625
8008
|
if (command === "trace") {
|
|
7626
8009
|
allowOptions(arguments_, ["follow"]);
|
|
7627
|
-
const id =
|
|
8010
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7628
8011
|
if (arguments_.options.has("follow")) {
|
|
7629
8012
|
await Effect10.runPromise(followTaskTrace(relay, id, printLine));
|
|
7630
8013
|
return;
|
|
@@ -7634,19 +8017,19 @@ var runCli = async (command, raw) => {
|
|
|
7634
8017
|
}
|
|
7635
8018
|
if (command === "wait") {
|
|
7636
8019
|
allowOptions(arguments_, ["timeout"]);
|
|
7637
|
-
const id =
|
|
8020
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7638
8021
|
print(taskResult(await Effect10.runPromise(waitForTask(relay, id, timeout(arguments_)))));
|
|
7639
8022
|
return;
|
|
7640
8023
|
}
|
|
7641
8024
|
if (command === "cancel") {
|
|
7642
8025
|
allowOptions(arguments_, []);
|
|
7643
|
-
const id =
|
|
8026
|
+
const id = Schema12.decodeUnknownSync(TaskId)(identifier(arguments_, "task id"));
|
|
7644
8027
|
print(taskResult(await Effect10.runPromise(relay.cancelTask(id))));
|
|
7645
8028
|
return;
|
|
7646
8029
|
}
|
|
7647
8030
|
if (command === "thread") {
|
|
7648
8031
|
allowOptions(arguments_, []);
|
|
7649
|
-
const id =
|
|
8032
|
+
const id = Schema12.decodeUnknownSync(ThreadId)(identifier(arguments_, "thread id"));
|
|
7650
8033
|
print(await Effect10.runPromise(threadContext(relay, id)));
|
|
7651
8034
|
return;
|
|
7652
8035
|
}
|
|
@@ -7655,15 +8038,15 @@ var runCli = async (command, raw) => {
|
|
|
7655
8038
|
|
|
7656
8039
|
// apps/device/src/daemon.ts
|
|
7657
8040
|
init_src();
|
|
7658
|
-
import { Effect as Effect11, Schedule as Schedule3, Schema as
|
|
7659
|
-
import { constants as
|
|
7660
|
-
import { open as
|
|
8041
|
+
import { Effect as Effect11, Schedule as Schedule3, Schema as Schema13 } from "effect";
|
|
8042
|
+
import { constants as constants5 } from "node:fs";
|
|
8043
|
+
import { open as open6, realpath as realpath4, stat as stat5 } from "node:fs/promises";
|
|
7661
8044
|
import { arch, hostname as hostname3, platform as platform4 } from "node:os";
|
|
7662
8045
|
import { basename as basename3, isAbsolute as isAbsolute5, relative as relative2, sep } from "node:path";
|
|
7663
8046
|
import { WebSocket as WebSocket2 } from "ws";
|
|
7664
8047
|
var maxQueuedRelayMessages = 8;
|
|
7665
8048
|
|
|
7666
|
-
class DeviceConnectionError extends
|
|
8049
|
+
class DeviceConnectionError extends Schema13.TaggedErrorClass()("Device.ConnectionError", { message: Schema13.String }) {
|
|
7667
8050
|
}
|
|
7668
8051
|
var socketUrl = (configuration) => {
|
|
7669
8052
|
const url = new URL(configuration.relayUrl);
|
|
@@ -7750,8 +8133,8 @@ var allowedWorkspace = async (configuration, requested) => {
|
|
|
7750
8133
|
var openAllowedWorkspace = async (configuration, requested) => {
|
|
7751
8134
|
const path = await allowedWorkspace(configuration, requested);
|
|
7752
8135
|
const operatingSystem2 = platform4();
|
|
7753
|
-
const flags =
|
|
7754
|
-
const handle = await
|
|
8136
|
+
const flags = constants5.O_RDONLY | (operatingSystem2 === "win32" ? 0 : constants5.O_DIRECTORY | constants5.O_NOFOLLOW);
|
|
8137
|
+
const handle = await open6(path, flags);
|
|
7755
8138
|
const identity = await handle.stat();
|
|
7756
8139
|
if (!identity.isDirectory()) {
|
|
7757
8140
|
await handle.close();
|
|
@@ -8184,9 +8567,9 @@ var connect = (configuration, state) => Effect11.tryPromise({
|
|
|
8184
8567
|
if (closed) {
|
|
8185
8568
|
return;
|
|
8186
8569
|
}
|
|
8187
|
-
const
|
|
8570
|
+
const text4 = Buffer.isBuffer(message) ? message.toString("utf8") : message instanceof ArrayBuffer ? Buffer.from(message).toString("utf8") : Array.isArray(message) ? Buffer.concat(message).toString("utf8") : Buffer.from(message).toString("utf8");
|
|
8188
8571
|
const event = await Effect11.runPromise(Effect11.try({
|
|
8189
|
-
try: () => JSON.parse(
|
|
8572
|
+
try: () => JSON.parse(text4),
|
|
8190
8573
|
catch: () => new DeviceConnectionError({ message: "Relay sent invalid JSON" })
|
|
8191
8574
|
}).pipe(Effect11.flatMap(decodeSocketEvent)));
|
|
8192
8575
|
if (event._tag === "Connected") {
|
|
@@ -8279,10 +8662,9 @@ var runDaemon = (configuration) => {
|
|
|
8279
8662
|
// apps/device/src/mcp.ts
|
|
8280
8663
|
init_src();
|
|
8281
8664
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8282
|
-
import {
|
|
8283
|
-
import { Effect as Effect12, Schema as Schema13 } from "effect";
|
|
8665
|
+
import { Effect as Effect12, Schema as Schema14 } from "effect";
|
|
8284
8666
|
import * as z from "zod/v4";
|
|
8285
|
-
import { writeFile as
|
|
8667
|
+
import { writeFile as writeFile9 } from "node:fs/promises";
|
|
8286
8668
|
|
|
8287
8669
|
// apps/device/src/mcp-build-notice.ts
|
|
8288
8670
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -8352,7 +8734,7 @@ var runMcp = async (configuration) => {
|
|
|
8352
8734
|
run_id: z.string().uuid().optional(),
|
|
8353
8735
|
question: z.string().min(1).max(maxClarificationBytes)
|
|
8354
8736
|
}
|
|
8355
|
-
}, async ({ task_id, run_id, question }) => output(await Effect12.runPromise(requestInput(client2, configuration,
|
|
8737
|
+
}, async ({ task_id, run_id, question }) => output(await Effect12.runPromise(requestInput(client2, configuration, Schema14.decodeUnknownSync(RequestTaskInput.fields.question)(question), task_id === undefined ? undefined : Schema14.decodeUnknownSync(TaskId)(task_id), run_id === undefined ? undefined : Schema14.decodeUnknownSync(TaskRunId)(run_id)))));
|
|
8356
8738
|
server.registerTool("task_answer", {
|
|
8357
8739
|
title: "Answer a worker's question",
|
|
8358
8740
|
description: "Answer the current clarification and resume the same task. Use input_request.id from task_status or delegate as request_id, or inputRequest.id from completion_inbox. Only the original requester or relay owner can answer. Use wait_task to collect the resumed result.",
|
|
@@ -8361,7 +8743,7 @@ var runMcp = async (configuration) => {
|
|
|
8361
8743
|
request_id: z.string().uuid(),
|
|
8362
8744
|
answer: z.string().min(1).max(maxClarificationBytes)
|
|
8363
8745
|
}
|
|
8364
|
-
}, async ({ task_id, request_id, answer }) => output(taskResult(await Effect12.runPromise(client2.answerTaskInput(
|
|
8746
|
+
}, async ({ task_id, request_id, answer }) => output(taskResult(await Effect12.runPromise(client2.answerTaskInput(Schema14.decodeUnknownSync(TaskId)(task_id), Schema14.decodeUnknownSync(AnswerTaskInput)({ requestId: request_id, answer }))))));
|
|
8365
8747
|
server.registerTool("task_progress", {
|
|
8366
8748
|
title: "Report task progress",
|
|
8367
8749
|
description: `Replace the latest progress note for a running task on this device. Use brief milestones, up to ${maxProgressNoteBytes} UTF-8 bytes. Inherits the delegated task ID when omitted.`,
|
|
@@ -8373,8 +8755,8 @@ var runMcp = async (configuration) => {
|
|
|
8373
8755
|
const id = task_id ?? configuration.mcpTaskId;
|
|
8374
8756
|
if (id === undefined)
|
|
8375
8757
|
throw new Error("Task id is required outside delegated work");
|
|
8376
|
-
const input =
|
|
8377
|
-
return output(await Effect12.runPromise(client2.reportTaskProgress(
|
|
8758
|
+
const input = Schema14.decodeUnknownSync(TaskProgressInput)({ note });
|
|
8759
|
+
return output(await Effect12.runPromise(client2.reportTaskProgress(Schema14.decodeUnknownSync(TaskId)(id), input)));
|
|
8378
8760
|
});
|
|
8379
8761
|
server.registerTool("list_devices", {
|
|
8380
8762
|
title: "List Cohall devices",
|
|
@@ -8420,7 +8802,7 @@ var runMcp = async (configuration) => {
|
|
|
8420
8802
|
const attachments2 = await readInputAttachments(attachment_paths ?? []);
|
|
8421
8803
|
const task = await Effect12.runPromise(createDelegation(client2, configuration, {
|
|
8422
8804
|
prompt,
|
|
8423
|
-
...request_id === undefined ? {} : { requestId:
|
|
8805
|
+
...request_id === undefined ? {} : { requestId: Schema14.decodeUnknownSync(TaskRequestId)(request_id) },
|
|
8424
8806
|
...provider === undefined ? {} : { provider },
|
|
8425
8807
|
...target === undefined ? {} : { target },
|
|
8426
8808
|
...context === undefined ? {} : { context },
|
|
@@ -8428,7 +8810,7 @@ var runMcp = async (configuration) => {
|
|
|
8428
8810
|
...parent_task_id === undefined ? {} : { parentTaskId: TaskId.make(parent_task_id) },
|
|
8429
8811
|
...workspace === undefined ? {} : { workspace },
|
|
8430
8812
|
...attachments2.length === 0 ? {} : { attachments: attachments2 },
|
|
8431
|
-
...deadline === undefined ? {} : { expiresAt:
|
|
8813
|
+
...deadline === undefined ? {} : { expiresAt: Schema14.decodeUnknownSync(TaskDeadline)(deadline) }
|
|
8432
8814
|
}), { signal });
|
|
8433
8815
|
const completed = wait ? await Effect12.runPromise(waitForTask(client2, task, timeout_seconds), { signal }) : task;
|
|
8434
8816
|
return output(wait ? await acknowledgedTaskResult(client2, completed, signal) : taskResult(completed));
|
|
@@ -8463,11 +8845,11 @@ var runMcp = async (configuration) => {
|
|
|
8463
8845
|
direction: z.enum(["input", "output"]).optional()
|
|
8464
8846
|
}
|
|
8465
8847
|
}, async ({ task_id, name, output_path, direction }, { signal }) => {
|
|
8466
|
-
const safeName =
|
|
8467
|
-
const safeDirection = direction === undefined ? undefined :
|
|
8848
|
+
const safeName = Schema14.decodeUnknownSync(AttachmentName)(name);
|
|
8849
|
+
const safeDirection = direction === undefined ? undefined : Schema14.decodeUnknownSync(AttachmentDirection)(direction);
|
|
8468
8850
|
const data = await Effect12.runPromise(client2.readAttachment(TaskId.make(task_id), safeName, safeDirection), { signal });
|
|
8469
8851
|
signal.throwIfAborted();
|
|
8470
|
-
await
|
|
8852
|
+
await writeFile9(output_path, data, { flag: "wx", mode: 384, signal });
|
|
8471
8853
|
return output({ task_id, name: safeName, output_path, bytes: data.length });
|
|
8472
8854
|
});
|
|
8473
8855
|
server.registerTool("task_trace", {
|
|
@@ -8497,7 +8879,7 @@ var runMcp = async (configuration) => {
|
|
|
8497
8879
|
description: "Read shared task prompts and final results for a cross-device thread.",
|
|
8498
8880
|
inputSchema: { thread_id: z.string().uuid() }
|
|
8499
8881
|
}, async ({ thread_id }) => output(await Effect12.runPromise(threadContext(client2, ThreadId.make(thread_id)))));
|
|
8500
|
-
await server.connect(
|
|
8882
|
+
await server.connect(createMcpHostDiagnostics(server.server));
|
|
8501
8883
|
};
|
|
8502
8884
|
|
|
8503
8885
|
// apps/device/src/main.ts
|
|
@@ -8553,4 +8935,4 @@ await main().catch((cause) => {
|
|
|
8553
8935
|
process.exitCode = 1;
|
|
8554
8936
|
});
|
|
8555
8937
|
|
|
8556
|
-
//# debugId=
|
|
8938
|
+
//# debugId=6DD87AF6E4752A1764756E2164756E21
|