aicomputer 0.1.13 → 0.1.15

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 CHANGED
@@ -8,6 +8,12 @@ Agent Computer CLI for creating, opening, and managing computers from the termin
8
8
  npm install -g aicomputer
9
9
  ```
10
10
 
11
+ Upgrade the installed CLI later with:
12
+
13
+ ```bash
14
+ computer upgrade
15
+ ```
16
+
11
17
  Or run it directly with Nix:
12
18
 
13
19
  ```bash
@@ -78,26 +84,29 @@ After installing, use the `computer` command:
78
84
 
79
85
  ```bash
80
86
  computer login
87
+ computer upgrade
81
88
  computer login --api-key <ac_live_...>
82
89
  computer claude-login
83
90
  computer codex-login
84
91
  computer whoami
85
92
  computer create my-box
86
93
  computer open my-box
87
- computer open my-box --terminal
88
94
  computer ssh
89
95
  computer ssh my-box
90
96
  computer ssh --setup
97
+ computer mount
98
+ computer mount status
91
99
  computer agent agents my-box
92
100
  computer agent sessions list my-box
93
- computer agent prompt my-box "inspect /workspace" --agent codex
94
- computer fleet status
101
+ computer agent prompt my-box "inspect /home/node" --agent codex
95
102
  computer acp serve my-box --agent codex
96
103
  ```
97
104
 
98
105
  `computer login` authenticates the CLI against Agent Computer. Use
99
106
  `computer claude-login` and `computer codex-login` to install Claude Code or
100
- Codex credentials onto a machine after the CLI is already logged in.
107
+ Codex credentials onto a machine after the CLI is already logged in. Use
108
+ `computer upgrade` to update a global npm install or the matching Nix profile
109
+ entry.
101
110
 
102
111
  Run `computer ssh` without a handle in an interactive terminal to pick from your available machines.
103
112
 
@@ -108,6 +117,11 @@ ssh agentcomputer.ai
108
117
  ssh my-box@agentcomputer.ai
109
118
  ```
110
119
 
111
- Use `computer agent` to inspect agents on one machine and manage remote sessions. Use `computer fleet status` to see active agent work across every machine in your fleet.
120
+ Run `computer mount` to start a foreground controller that mirrors all SSH-ready
121
+ machine homes under `~/agentcomputer/<handle>` while the command is running.
122
+ This uses the same SSH setup as `computer ssh --setup` and requires Mutagen plus
123
+ OpenSSH client tools (`ssh` and `scp`) to already be installed locally.
124
+
125
+ Use `computer agent` to inspect agents on one machine and manage remote sessions. Use `computer acp serve` when you want to expose one remote session through a local ACP bridge.
112
126
 
113
127
  You can also run without a global install via `npx aicomputer <command>`.
@@ -0,0 +1,186 @@
1
+ // src/lib/mount-config.ts
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ renameSync,
7
+ rmSync,
8
+ writeFileSync
9
+ } from "fs";
10
+ import { homedir } from "os";
11
+ import { join } from "path";
12
+ var MOUNT_SERVICE_LABEL = "ai.agentcomputer.mount";
13
+ function getDefaultMountRoot() {
14
+ return join(homedir(), "agentcomputer");
15
+ }
16
+ function getMountStateDir() {
17
+ return join(homedir(), ".computer", "mount");
18
+ }
19
+ function getMountPaths(rootPath = getDefaultMountRoot()) {
20
+ const stateDir = getMountStateDir();
21
+ return {
22
+ stateDir,
23
+ rootPath,
24
+ configPath: join(stateDir, "config.json"),
25
+ statusPath: join(stateDir, "status.json"),
26
+ socketPath: join(stateDir, "control.sock"),
27
+ controllerLockPath: join(stateDir, "controller.lock.json"),
28
+ handlesDir: join(stateDir, "handles"),
29
+ sshToolsDir: join(stateDir, "ssh-tools"),
30
+ staleDir: join(stateDir, "stale")
31
+ };
32
+ }
33
+ function getMountHandlePaths(handle, rootPath = getDefaultMountRoot()) {
34
+ const paths = getMountPaths(rootPath);
35
+ const stateDir = join(paths.handlesDir, handle);
36
+ return {
37
+ stateDir,
38
+ projectFilePath: join(stateDir, "mutagen.yml"),
39
+ metaPath: join(stateDir, "meta.json"),
40
+ sshToolsDir: join(stateDir, "ssh-tools")
41
+ };
42
+ }
43
+ function defaultMountServiceConfig() {
44
+ return {
45
+ alias: "agentcomputer.ai",
46
+ host: "ssh.agentcomputer.ai",
47
+ port: 443,
48
+ rootPath: getDefaultMountRoot(),
49
+ pollIntervalMs: 5e3,
50
+ connectTimeoutSeconds: 5
51
+ };
52
+ }
53
+ function ensureMountDirectories(paths) {
54
+ for (const directory of [
55
+ paths.stateDir,
56
+ paths.rootPath,
57
+ paths.handlesDir,
58
+ paths.sshToolsDir,
59
+ paths.staleDir
60
+ ]) {
61
+ if (!existsSync(directory)) {
62
+ mkdirSync(directory, { recursive: true, mode: 448 });
63
+ }
64
+ }
65
+ }
66
+ function ensureHandleDirectories(handle, rootPath) {
67
+ const handlePaths = getMountHandlePaths(handle, rootPath);
68
+ for (const directory of [handlePaths.stateDir, handlePaths.sshToolsDir]) {
69
+ if (!existsSync(directory)) {
70
+ mkdirSync(directory, { recursive: true, mode: 448 });
71
+ }
72
+ }
73
+ return handlePaths;
74
+ }
75
+ function readMountConfig() {
76
+ const defaults = defaultMountServiceConfig();
77
+ const paths = getMountPaths(defaults.rootPath);
78
+ if (!existsSync(paths.configPath)) {
79
+ return null;
80
+ }
81
+ try {
82
+ const raw = JSON.parse(readFileSync(paths.configPath, "utf8"));
83
+ return {
84
+ alias: typeof raw.alias === "string" && raw.alias.trim() ? raw.alias.trim() : defaults.alias,
85
+ host: typeof raw.host === "string" && raw.host.trim() ? raw.host.trim() : defaults.host,
86
+ port: typeof raw.port === "number" ? raw.port : defaults.port,
87
+ rootPath: typeof raw.rootPath === "string" && raw.rootPath.trim() ? raw.rootPath.trim() : defaults.rootPath,
88
+ pollIntervalMs: typeof raw.pollIntervalMs === "number" && raw.pollIntervalMs > 0 ? raw.pollIntervalMs : defaults.pollIntervalMs,
89
+ connectTimeoutSeconds: typeof raw.connectTimeoutSeconds === "number" && raw.connectTimeoutSeconds > 0 ? raw.connectTimeoutSeconds : defaults.connectTimeoutSeconds
90
+ };
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ function writeMountConfig(config) {
96
+ const paths = getMountPaths(config.rootPath);
97
+ ensureMountDirectories(paths);
98
+ writeJSONFile(paths.configPath, config);
99
+ }
100
+ function readMountStatusSnapshot(rootPath = getDefaultMountRoot()) {
101
+ const paths = getMountPaths(rootPath);
102
+ if (!existsSync(paths.statusPath)) {
103
+ return null;
104
+ }
105
+ try {
106
+ return JSON.parse(readFileSync(paths.statusPath, "utf8"));
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+ function writeMountStatusSnapshot(snapshot, rootPath = getDefaultMountRoot()) {
112
+ const paths = getMountPaths(rootPath);
113
+ ensureMountDirectories(paths);
114
+ writeJSONFile(paths.statusPath, snapshot);
115
+ }
116
+ function readMountControllerLock(rootPath = getDefaultMountRoot()) {
117
+ const paths = getMountPaths(rootPath);
118
+ if (!existsSync(paths.controllerLockPath)) {
119
+ return null;
120
+ }
121
+ try {
122
+ return JSON.parse(readFileSync(paths.controllerLockPath, "utf8"));
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+ function writeMountControllerLock(lock, rootPath = getDefaultMountRoot()) {
128
+ const paths = getMountPaths(rootPath);
129
+ ensureMountDirectories(paths);
130
+ writeJSONFile(paths.controllerLockPath, lock);
131
+ }
132
+ function removeMountControllerLock(rootPath = getDefaultMountRoot()) {
133
+ const paths = getMountPaths(rootPath);
134
+ try {
135
+ rmSync(paths.controllerLockPath, { force: true });
136
+ } catch {
137
+ }
138
+ }
139
+ function readMountHandleMeta(handle, rootPath = getDefaultMountRoot()) {
140
+ const paths = getMountHandlePaths(handle, rootPath);
141
+ if (!existsSync(paths.metaPath)) {
142
+ return null;
143
+ }
144
+ try {
145
+ return JSON.parse(readFileSync(paths.metaPath, "utf8"));
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+ function writeMountHandleMeta(handle, meta, rootPath = getDefaultMountRoot()) {
151
+ const paths = ensureHandleDirectories(handle, rootPath);
152
+ writeJSONFile(paths.metaPath, meta);
153
+ }
154
+ function removeMountHandleState(handle, rootPath = getDefaultMountRoot()) {
155
+ const paths = getMountHandlePaths(handle, rootPath);
156
+ try {
157
+ rmSync(paths.stateDir, { recursive: true, force: true });
158
+ } catch {
159
+ }
160
+ }
161
+ function writeJSONFile(path, value) {
162
+ const tempFile = `${path}.${process.pid}.tmp`;
163
+ writeFileSync(tempFile, JSON.stringify(value, null, 2), { mode: 384 });
164
+ renameSync(tempFile, path);
165
+ }
166
+
167
+ export {
168
+ MOUNT_SERVICE_LABEL,
169
+ getDefaultMountRoot,
170
+ getMountStateDir,
171
+ getMountPaths,
172
+ getMountHandlePaths,
173
+ defaultMountServiceConfig,
174
+ ensureMountDirectories,
175
+ ensureHandleDirectories,
176
+ readMountConfig,
177
+ writeMountConfig,
178
+ readMountStatusSnapshot,
179
+ writeMountStatusSnapshot,
180
+ readMountControllerLock,
181
+ writeMountControllerLock,
182
+ removeMountControllerLock,
183
+ readMountHandleMeta,
184
+ writeMountHandleMeta,
185
+ removeMountHandleState
186
+ };