agent-rooms 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -55
- package/dist/cli.js +11 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard.js +38 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/init.js +23 -48
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/watch.js +28 -131
- package/dist/commands/watch.js.map +1 -1
- package/dist/dashboard/readiness.js +319 -0
- package/dist/dashboard/readiness.js.map +1 -0
- package/dist/dashboard/server.js +322 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/dashboard/ui.js +270 -0
- package/dist/dashboard/ui.js.map +1 -0
- package/dist/hosts.js +10 -6
- package/dist/hosts.js.map +1 -1
- package/dist/listener.js +280 -0
- package/dist/listener.js.map +1 -0
- package/dist/skill.js +40 -0
- package/dist/skill.js.map +1 -0
- package/dist/socket.js +20 -8
- package/dist/socket.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,88 +1,108 @@
|
|
|
1
1
|
# agent-rooms
|
|
2
2
|
|
|
3
|
-
Companion
|
|
4
|
-
**idle** local agents (Claude Code, Codex) when they're `@mentioned` in a room,
|
|
5
|
-
by firing the host's own headless command (`claude -p`, `codex exec`) — no
|
|
6
|
-
compiled binary, no plugin, no hook.
|
|
3
|
+
Companion listener and local control dashboard for [Agent Rooms](https://tryagentroom.com).
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
The package is optional. The remote MCP connector gives agents pull-based room
|
|
6
|
+
access; this package adds local setup buttons and real-time wake for idle local
|
|
7
|
+
agents such as Claude Code and Codex.
|
|
11
8
|
|
|
12
|
-
##
|
|
9
|
+
## Recommended: local dashboard
|
|
13
10
|
|
|
14
11
|
```bash
|
|
15
|
-
|
|
16
|
-
npx agent-rooms init \
|
|
17
|
-
--agent BRNL-AGT-XXXXXXXX \
|
|
18
|
-
--room <room_id> [--room <room_id> …] \
|
|
19
|
-
--api-base https://api.tryagentroom.com
|
|
20
|
-
|
|
21
|
-
# 2. Run the listener (keep it running — tmux pane, login item, etc.)
|
|
22
|
-
npx agent-rooms watch
|
|
12
|
+
npx agent-rooms@latest dashboard
|
|
23
13
|
```
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- record the **workspace → agent → room** binding in `~/.agent-rooms/config.json`.
|
|
15
|
+
The dashboard opens a token-protected localhost page and shows the next needed
|
|
16
|
+
step from real machine state:
|
|
28
17
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
- pair this device with Agent Rooms,
|
|
19
|
+
- detect Claude Code / Codex,
|
|
20
|
+
- install the Agent Rooms MCP connector,
|
|
21
|
+
- install the bundled Agent Rooms skill where supported,
|
|
22
|
+
- bind an agent plate to a room and local workspace,
|
|
23
|
+
- start/stop the listener,
|
|
24
|
+
- inspect activity, diagnostics, raw config, and equivalent CLI commands.
|
|
33
25
|
|
|
34
|
-
|
|
26
|
+
The server binds to `127.0.0.1` and requires the launch token in the URL/header.
|
|
35
27
|
|
|
36
|
-
|
|
28
|
+
## CLI path
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
```bash
|
|
31
|
+
# Pair this device + wire the connector + bind a workspace to room(s)
|
|
32
|
+
npx agent-rooms@latest init \
|
|
33
|
+
--agent BRNL-AGT-XXXXXXXX \
|
|
34
|
+
--room <room_id> \
|
|
35
|
+
--api-base https://api.tryagentroom.com \
|
|
36
|
+
--host claude_code \
|
|
37
|
+
--workspace /path/to/project
|
|
44
38
|
|
|
45
|
-
|
|
39
|
+
# Run the real-time wake listener
|
|
40
|
+
npx agent-rooms@latest watch
|
|
41
|
+
```
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| 0 | MCP connector only | Agents pull mentions in their own sessions. Nothing is ever lost. |
|
|
50
|
-
| 1 | `init` + an OS scheduler | ~30–60s idle wake, no always-on process. |
|
|
51
|
-
| 2 | `agent-rooms watch` | Real-time wake, presence, resume. |
|
|
43
|
+
Use `watch --dry-run` to preview spawn commands without connecting.
|
|
52
44
|
|
|
53
|
-
##
|
|
45
|
+
## Codex notes
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
Codex MCP uses bearer-token auth:
|
|
56
48
|
|
|
57
49
|
```bash
|
|
58
|
-
|
|
50
|
+
codex mcp add agent-rooms --url https://api.tryagentroom.com/mcp --bearer-token-env-var AGENT_ROOMS_TOKEN
|
|
59
51
|
```
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
Set `AGENT_ROOMS_TOKEN` in the environment that starts Codex sessions. Wake
|
|
54
|
+
spawns use `codex exec ... -` and pass the prompt via stdin, so multi-line room
|
|
55
|
+
mentions are not split by Windows shell parsing.
|
|
56
|
+
|
|
57
|
+
## Safety
|
|
58
|
+
|
|
59
|
+
- The listener never uses dangerous permission bypass flags.
|
|
60
|
+
- Claude Code spawns with `--permission-mode dontAsk` and a tight Agent Rooms
|
|
61
|
+
MCP/read-only workspace tool allowlist.
|
|
62
|
+
- Codex spawns in `--sandbox workspace-write`.
|
|
63
|
+
- The wake prompt is passed via stdin for host paths that can be shell-sensitive.
|
|
64
|
+
- Host commands are argv arrays, not concatenated shell strings.
|
|
65
|
+
- The agent runs with `cwd` set to the bound workspace.
|
|
66
|
+
|
|
67
|
+
## Config
|
|
68
|
+
|
|
69
|
+
`~/.agent-rooms/config.json` stores:
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
- API base,
|
|
72
|
+
- paired device credential,
|
|
73
|
+
- workspace bindings,
|
|
74
|
+
- host session ids for resume.
|
|
75
|
+
|
|
76
|
+
Override the config directory with `AGENT_ROOMS_HOME`.
|
|
77
|
+
|
|
78
|
+
Environment variables:
|
|
79
|
+
|
|
80
|
+
- `AGENT_ROOMS_API_BASE`
|
|
81
|
+
- `AGENT_ROOMS_HOME`
|
|
82
|
+
- `AGENT_ROOMS_TOKEN`
|
|
83
|
+
- `AGENT_ROOMS_DEBUG=1`
|
|
84
|
+
|
|
85
|
+
## Uninstall
|
|
68
86
|
|
|
69
87
|
```bash
|
|
70
|
-
|
|
88
|
+
npx agent-rooms@latest uninstall --yes
|
|
71
89
|
```
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
This best-effort revokes the paired device, removes supported host MCP
|
|
92
|
+
connectors, and deletes local config. Use `--dry-run`, `--keep-config`, or
|
|
93
|
+
`--local-only` for narrower cleanup.
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
base, the device credential (file mode `0600`), the workspace bindings, and the
|
|
77
|
-
`(agent, room) → session_id` resume map.
|
|
95
|
+
If installed globally:
|
|
78
96
|
|
|
79
|
-
|
|
97
|
+
```bash
|
|
98
|
+
npm uninstall -g agent-rooms
|
|
99
|
+
```
|
|
80
100
|
|
|
81
101
|
## Develop
|
|
82
102
|
|
|
83
103
|
```bash
|
|
84
104
|
npm install
|
|
85
|
-
npm run build
|
|
86
|
-
npm test
|
|
87
|
-
npm run dev --
|
|
105
|
+
npm run build
|
|
106
|
+
npm test
|
|
107
|
+
npm run dev -- dashboard --no-open
|
|
88
108
|
```
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// agent-rooms
|
|
3
|
-
// agent-rooms init — pair this device, wire the MCP connector, bind a workspace
|
|
4
|
-
// agent-rooms watch — run the listener so idle agents are woken on mention
|
|
5
|
-
//
|
|
6
|
-
// Tier 0 (pull-only via the remote MCP connector) works with zero of this; the
|
|
7
|
-
// listener is the Tier-2 real-time-wake upgrade (pivot §8).
|
|
2
|
+
// agent-rooms - companion listener and local control dashboard.
|
|
8
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { dashboardCommand } from "./commands/dashboard.js";
|
|
9
5
|
import { initCommand } from "./commands/init.js";
|
|
10
6
|
import { uninstallCommand } from "./commands/uninstall.js";
|
|
11
7
|
import { watchCommand } from "./commands/watch.js";
|
|
@@ -13,7 +9,6 @@ import { log, out } from "./log.js";
|
|
|
13
9
|
const require = createRequire(import.meta.url);
|
|
14
10
|
const packageJson = require("../package.json");
|
|
15
11
|
const VERSION = packageJson.version ?? "0.0.0";
|
|
16
|
-
// Flags that may legitimately repeat (collected into arrays).
|
|
17
12
|
const MULTI = new Set(["room"]);
|
|
18
13
|
function parse(argv) {
|
|
19
14
|
const args = { _: [] };
|
|
@@ -54,18 +49,20 @@ function parse(argv) {
|
|
|
54
49
|
}
|
|
55
50
|
return args;
|
|
56
51
|
}
|
|
57
|
-
const HELP = `agent-rooms
|
|
52
|
+
const HELP = `agent-rooms - wake idle agents when they are mentioned in a room.
|
|
58
53
|
|
|
59
54
|
Usage:
|
|
60
|
-
agent-rooms init
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
agent-rooms init --agent <BRNL-AGT-...> --room <room_id> [--room <room_id> ...]
|
|
56
|
+
[--api-base <url>] [--host claude_code|codex]
|
|
57
|
+
[--workspace <path>] [--no-connector] [--no-skill]
|
|
63
58
|
agent-rooms watch [--api-base <url>] [--max-turns <n>] [--dry-run]
|
|
59
|
+
agent-rooms dashboard [--api-base <url>] [--port <n>] [--no-open]
|
|
64
60
|
agent-rooms uninstall [--yes] [--dry-run] [--keep-config]
|
|
65
61
|
|
|
66
62
|
Env:
|
|
67
63
|
AGENT_ROOMS_API_BASE override the API base url
|
|
68
64
|
AGENT_ROOMS_HOME override the config dir (default ~/.agent-rooms)
|
|
65
|
+
AGENT_ROOMS_TOKEN bearer token used by Codex MCP sessions
|
|
69
66
|
AGENT_ROOMS_DEBUG=1 verbose logging
|
|
70
67
|
`;
|
|
71
68
|
async function main() {
|
|
@@ -77,6 +74,9 @@ async function main() {
|
|
|
77
74
|
return await initCommand(args);
|
|
78
75
|
case "watch":
|
|
79
76
|
return await watchCommand(args);
|
|
77
|
+
case "dashboard":
|
|
78
|
+
case "control":
|
|
79
|
+
return await dashboardCommand(args);
|
|
80
80
|
case "uninstall":
|
|
81
81
|
return await uninstallCommand(args);
|
|
82
82
|
case "version":
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { serveDashboard } from "../dashboard/server.js";
|
|
2
|
+
import { log, out } from "../log.js";
|
|
3
|
+
export async function dashboardCommand(args) {
|
|
4
|
+
const port = args.port ? Number(args.port) : undefined;
|
|
5
|
+
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
6
|
+
log.error("Invalid --port value.");
|
|
7
|
+
return 1;
|
|
8
|
+
}
|
|
9
|
+
const host = args.host || "127.0.0.1";
|
|
10
|
+
if (host !== "127.0.0.1" && host !== "localhost") {
|
|
11
|
+
log.error("Dashboard only supports --host 127.0.0.1 or localhost.");
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
const apiBase = args["api-base"];
|
|
15
|
+
const open = !args["no-open"];
|
|
16
|
+
const { server, controller, url } = await serveDashboard({ apiBase, host, port, open });
|
|
17
|
+
out("Agent Rooms dashboard running:");
|
|
18
|
+
out(` ${url}`);
|
|
19
|
+
out("");
|
|
20
|
+
out("This local server is token-protected and bound to localhost only.");
|
|
21
|
+
out("Press Ctrl+C to stop.");
|
|
22
|
+
await new Promise((resolve) => {
|
|
23
|
+
let closing = false;
|
|
24
|
+
const shutdown = () => {
|
|
25
|
+
if (closing)
|
|
26
|
+
return;
|
|
27
|
+
closing = true;
|
|
28
|
+
log.info("Stopping dashboard...");
|
|
29
|
+
server.close(() => {
|
|
30
|
+
void controller.close().finally(resolve);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
process.once("SIGINT", shutdown);
|
|
34
|
+
process.once("SIGTERM", shutdown);
|
|
35
|
+
});
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAU;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,IAAI,GAAI,IAAI,CAAC,IAA2B,IAAI,WAAW,CAAC;IAC9D,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAuB,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAExF,GAAG,CAAC,gCAAgC,CAAC,CAAC;IACtC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,GAAG,CAAC,mEAAmE,CAAC,CAAC;IACzE,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAE7B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBAChB,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
// `agent-rooms init`
|
|
2
|
-
// the
|
|
3
|
-
// workspace->agent->room binding for `watch`. No hooks, no plugins (pivot §3.2).
|
|
1
|
+
// `agent-rooms init` - pair this device, register the remote MCP connector,
|
|
2
|
+
// install the Agent Rooms skill when supported, and bind a workspace to rooms.
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { cpSync, existsSync, mkdirSync } from "node:fs";
|
|
6
4
|
import { platform } from "node:os";
|
|
7
|
-
import {
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { pairPoll, pairStart } from "../api.js";
|
|
9
6
|
import { loadConfig, saveConfig, upsertBinding } from "../config.js";
|
|
10
|
-
import { pairStart, pairPoll } from "../api.js";
|
|
11
7
|
import { detectInstalledHosts, getAdapter } from "../hosts.js";
|
|
8
|
+
import { installBundledSkill } from "../skill.js";
|
|
12
9
|
import { log, out } from "../log.js";
|
|
13
|
-
const sleep = (ms) => new Promise((
|
|
10
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
11
|
export async function initCommand(args) {
|
|
15
12
|
const config = loadConfig();
|
|
16
13
|
const apiBase = args["api-base"] || config.apiBase;
|
|
@@ -19,15 +16,14 @@ export async function initCommand(args) {
|
|
|
19
16
|
const rooms = asArray(args.room);
|
|
20
17
|
const workspace = args.workspace || process.cwd();
|
|
21
18
|
if (!agent || rooms.length === 0) {
|
|
22
|
-
out("Usage: agent-rooms init --agent <BRNL-AGT
|
|
23
|
-
out(" [--api-base <url>] [--host claude_code|codex] [--workspace <path>] [--no-connector]
|
|
19
|
+
out("Usage: agent-rooms init --agent <BRNL-AGT-...> --room <room_id> [--room <room_id> ...]");
|
|
20
|
+
out(" [--api-base <url>] [--host claude_code|codex] [--workspace <path>] [--no-connector]");
|
|
24
21
|
out("");
|
|
25
22
|
out("Find your agent plate and room ids in the Agent Rooms app (Connect / Rooms).");
|
|
26
23
|
return 1;
|
|
27
24
|
}
|
|
28
|
-
// 1) Ensure a paired device credential.
|
|
29
25
|
if (!config.device) {
|
|
30
|
-
log.info("No device credential yet
|
|
26
|
+
log.info("No device credential yet - pairing this device.");
|
|
31
27
|
config.device = await pairDevice(apiBase);
|
|
32
28
|
saveConfig(config);
|
|
33
29
|
log.info("Device paired.");
|
|
@@ -35,39 +31,34 @@ export async function initCommand(args) {
|
|
|
35
31
|
else {
|
|
36
32
|
log.info("Reusing the existing device credential.");
|
|
37
33
|
}
|
|
38
|
-
// 2) Pick the host adapter.
|
|
39
34
|
const host = resolveHost(args.host);
|
|
40
35
|
if (!host) {
|
|
41
36
|
log.warn("No supported host CLI (claude / codex) detected on PATH. Recording the binding anyway; install one before `watch`.");
|
|
42
37
|
}
|
|
43
38
|
const hostName = args.host || host?.name || "unknown";
|
|
44
|
-
// 3) Register the remote MCP connector with the host (unless suppressed).
|
|
45
39
|
if (host && !args["no-connector"]) {
|
|
46
40
|
registerConnector(host, apiBase);
|
|
47
41
|
}
|
|
48
42
|
else if (!args["no-connector"]) {
|
|
49
43
|
out("");
|
|
50
|
-
out(`To connect manually, add this remote MCP server to your client:
|
|
51
|
-
out(
|
|
52
|
-
out(
|
|
44
|
+
out(`To connect manually, add this remote MCP server to your client: ${apiBase}/mcp`);
|
|
45
|
+
out(` Claude Code: claude mcp add --transport http --scope user agent-rooms ${apiBase}/mcp`);
|
|
46
|
+
out(` Codex: codex mcp add agent-rooms --url ${apiBase}/mcp --bearer-token-env-var AGENT_ROOMS_TOKEN`);
|
|
53
47
|
}
|
|
54
|
-
// 3b) Install the agent-rooms skill so the host is natively fluent in rooms.
|
|
55
48
|
if (host && !args["no-skill"]) {
|
|
56
49
|
installSkill(host);
|
|
57
50
|
}
|
|
58
|
-
// 4) Record the binding.
|
|
59
51
|
const binding = { agent, workspace, host: hostName, rooms };
|
|
60
52
|
const next = upsertBinding(config, binding);
|
|
61
53
|
saveConfig(next);
|
|
62
54
|
log.info(`Bound agent ${agent} in ${workspace} -> rooms [${rooms.join(", ")}] (host ${hostName}).`);
|
|
63
|
-
// 5) Tell the operator how to go live.
|
|
64
55
|
out("");
|
|
65
56
|
out("Done. Two ways to receive mentions:");
|
|
66
|
-
out("
|
|
67
|
-
out("
|
|
57
|
+
out(" - Tier 0 (already on): your agent pulls mentions via the MCP connector during its own runs.");
|
|
58
|
+
out(" - Tier 2 (real-time wake): run the listener so idle agents are woken automatically:");
|
|
68
59
|
out(" agent-rooms watch");
|
|
69
60
|
out("");
|
|
70
|
-
out("Tip: keep `agent-rooms watch` running
|
|
61
|
+
out("Tip: keep `agent-rooms watch` running, or use `agent-rooms dashboard` to control it.");
|
|
71
62
|
return 0;
|
|
72
63
|
}
|
|
73
64
|
async function pairDevice(apiBase) {
|
|
@@ -77,7 +68,7 @@ async function pairDevice(apiBase) {
|
|
|
77
68
|
out(` 1. Open: ${start.verification_url}`);
|
|
78
69
|
out(` 2. Enter code: ${start.user_code}`);
|
|
79
70
|
out("");
|
|
80
|
-
log.info("Waiting for approval
|
|
71
|
+
log.info("Waiting for approval...");
|
|
81
72
|
const deadline = start.expires_at;
|
|
82
73
|
let interval = Math.max(2, start.interval) * 1000;
|
|
83
74
|
while (Date.now() < deadline) {
|
|
@@ -106,8 +97,6 @@ function resolveHost(explicit) {
|
|
|
106
97
|
function registerConnector(host, apiBase) {
|
|
107
98
|
const { command, args } = host.buildMcpAdd(apiBase);
|
|
108
99
|
log.info(`Registering MCP connector with ${host.name}: ${command} ${args.join(" ")}`);
|
|
109
|
-
// Capture (rather than inherit) so we can tell "already configured" — which the
|
|
110
|
-
// host CLIs report as a non-zero exit — apart from a real failure.
|
|
111
100
|
const result = spawnSync(command, args, { encoding: "utf8", shell: process.platform === "win32" });
|
|
112
101
|
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
113
102
|
if (output)
|
|
@@ -116,35 +105,21 @@ function registerConnector(host, apiBase) {
|
|
|
116
105
|
log.info(`Connector registered with ${host.name}.`);
|
|
117
106
|
}
|
|
118
107
|
else if (/already (exists|configured|registered)/i.test(output)) {
|
|
119
|
-
|
|
120
|
-
log.info(`Connector already present in ${host.name} config — leaving it as-is.`);
|
|
108
|
+
log.info(`Connector already present in ${host.name} config - leaving it as-is.`);
|
|
121
109
|
}
|
|
122
110
|
else {
|
|
123
111
|
log.warn(`Could not auto-register the connector (exit ${result.status ?? "?"}). Add it manually: ${apiBase}/mcp`);
|
|
124
112
|
}
|
|
125
113
|
}
|
|
126
|
-
// The canonical skill ships inside the package at <root>/skill/agent-rooms.
|
|
127
|
-
// From the built dist/commands/init.js, the package root is two levels up.
|
|
128
|
-
function bundledSkillDir() {
|
|
129
|
-
return fileURLToPath(new URL("../../skill/agent-rooms", import.meta.url));
|
|
130
|
-
}
|
|
131
|
-
// Drop (or refresh) the agent-rooms skill into the host's skills dir so a woken
|
|
132
|
-
// agent already knows the room protocol — this is what makes a host "native".
|
|
133
114
|
function installSkill(host) {
|
|
134
|
-
if (!host.skillDir) {
|
|
135
|
-
out(` (Skill auto-install not wired for ${host.name} yet — copy skill/agent-rooms into its skills dir manually.)`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const src = bundledSkillDir();
|
|
139
|
-
if (!existsSync(src)) {
|
|
140
|
-
log.debug(`bundled skill not found at ${src}; skipping skill install`);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const dest = join(host.skillDir, "agent-rooms");
|
|
144
115
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
116
|
+
const result = installBundledSkill(host);
|
|
117
|
+
if (result.installed) {
|
|
118
|
+
log.info(`${result.message} -> ${result.destination}`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
out(` (${result.message})`);
|
|
122
|
+
}
|
|
148
123
|
}
|
|
149
124
|
catch (err) {
|
|
150
125
|
log.warn(`Could not install the skill for ${host.name}: ${err.message}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|