@easonwumac/computer-linker 0.1.2
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 +230 -0
- package/LICENSE +21 -0
- package/README.md +539 -0
- package/SECURITY.md +48 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +360 -0
- package/dist/audit.d.ts +70 -0
- package/dist/audit.js +102 -0
- package/dist/capabilities.d.ts +98 -0
- package/dist/capabilities.js +718 -0
- package/dist/capability-policy.d.ts +22 -0
- package/dist/capability-policy.js +103 -0
- package/dist/chatgpt.d.ts +167 -0
- package/dist/chatgpt.js +561 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +4621 -0
- package/dist/client-smoke.d.ts +44 -0
- package/dist/client-smoke.js +639 -0
- package/dist/client.d.ts +217 -0
- package/dist/client.js +357 -0
- package/dist/codex-runs.d.ts +35 -0
- package/dist/codex-runs.js +66 -0
- package/dist/computer-contract.d.ts +33 -0
- package/dist/computer-contract.js +384 -0
- package/dist/computer-operation-registry.d.ts +45 -0
- package/dist/computer-operation-registry.js +179 -0
- package/dist/config-diagnostics.d.ts +11 -0
- package/dist/config-diagnostics.js +185 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +69 -0
- package/dist/history-insights.d.ts +132 -0
- package/dist/history-insights.js +457 -0
- package/dist/http-auth.d.ts +3 -0
- package/dist/http-auth.js +15 -0
- package/dist/mcp-surface.d.ts +5 -0
- package/dist/mcp-surface.js +25 -0
- package/dist/oauth-provider.d.ts +52 -0
- package/dist/oauth-provider.js +325 -0
- package/dist/package-metadata.d.ts +7 -0
- package/dist/package-metadata.js +24 -0
- package/dist/permissions.d.ts +43 -0
- package/dist/permissions.js +150 -0
- package/dist/platform-shell.d.ts +28 -0
- package/dist/platform-shell.js +124 -0
- package/dist/processes.d.ts +50 -0
- package/dist/processes.js +178 -0
- package/dist/profile.d.ts +159 -0
- package/dist/profile.js +416 -0
- package/dist/screenshot.d.ts +47 -0
- package/dist/screenshot.js +302 -0
- package/dist/search.d.ts +34 -0
- package/dist/search.js +340 -0
- package/dist/security.d.ts +10 -0
- package/dist/security.js +108 -0
- package/dist/sensitive-files.d.ts +4 -0
- package/dist/sensitive-files.js +96 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +713 -0
- package/dist/service.d.ts +125 -0
- package/dist/service.js +486 -0
- package/dist/sessions.d.ts +26 -0
- package/dist/sessions.js +34 -0
- package/dist/tunnels.d.ts +161 -0
- package/dist/tunnels.js +1243 -0
- package/dist/workspace-operations.d.ts +170 -0
- package/dist/workspace-operations.js +3219 -0
- package/dist/workspaces.d.ts +61 -0
- package/dist/workspaces.js +353 -0
- package/docs/agent-instructions.md +65 -0
- package/docs/alpha-evidence.example.json +54 -0
- package/docs/api-compatibility.md +56 -0
- package/docs/architecture.md +561 -0
- package/docs/chatgpt-setup.md +397 -0
- package/docs/client-recipes.md +98 -0
- package/docs/client-sdk.md +163 -0
- package/docs/computer-operation-v1.schema.json +143 -0
- package/docs/manual-test-plan.md +322 -0
- package/docs/product-spec.md +911 -0
- package/docs/release-checklist.md +285 -0
- package/docs/service-mode.md +99 -0
- package/examples/minimal-mcp-client.mjs +114 -0
- package/package.json +87 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Release Checklist
|
|
2
|
+
|
|
3
|
+
Computer Linker's first productized target is an alpha release for trusted
|
|
4
|
+
local development machines. Do not treat alpha as an unattended public service.
|
|
5
|
+
|
|
6
|
+
## Alpha Gate
|
|
7
|
+
|
|
8
|
+
Run the same gate locally and in CI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm run release:validate
|
|
12
|
+
npm run product:check
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This performs:
|
|
16
|
+
|
|
17
|
+
- Release metadata validation.
|
|
18
|
+
- TypeScript typecheck.
|
|
19
|
+
- Full test suite.
|
|
20
|
+
- Runtime build.
|
|
21
|
+
- Package smoke check with `npm pack --dry-run`, real `.tgz` creation,
|
|
22
|
+
temporary consumer install, installed CLI execution, isolated installed
|
|
23
|
+
`self-test`, `setup` / `status` config smoke, and installed SDK import.
|
|
24
|
+
|
|
25
|
+
Before publishing a public npm/package artifact, run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run public:check
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Local npm Release Automation
|
|
32
|
+
|
|
33
|
+
Use the local wrapper when you are ready to publish from the current repository:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run release:check
|
|
37
|
+
npm run release:dry-run
|
|
38
|
+
npm run release:publish -- --create-tag --otp <code>
|
|
39
|
+
git push origin main --follow-tags
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`release:check` runs the local product and public package gates without
|
|
43
|
+
publishing. It does not require a clean worktree, so it is useful before the
|
|
44
|
+
final release commit.
|
|
45
|
+
|
|
46
|
+
`release:dry-run` requires the release commit to be clean and on main/master.
|
|
47
|
+
It runs `npm publish --dry-run`; if `v<package.version>` is missing, it creates
|
|
48
|
+
a temporary local tag for the dry-run and removes it afterward. If that tag
|
|
49
|
+
already exists on another commit, bump the package version before releasing.
|
|
50
|
+
|
|
51
|
+
`release:publish` performs the real npm publish. It requires a clean
|
|
52
|
+
main/master worktree, a dated `CHANGELOG.md` heading for the package version,
|
|
53
|
+
npm login, and a release tag on `HEAD`. Pass `--create-tag` to create the
|
|
54
|
+
release tag automatically before publishing, `--otp <code>` for npm 2FA, and
|
|
55
|
+
`--push` only when the script should push `HEAD` and tags to `origin` after a
|
|
56
|
+
successful publish.
|
|
57
|
+
|
|
58
|
+
For the one-command local alpha readiness gate, run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run alpha:check
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This runs `product:check`, `public:audit`, a preserved-history public repo
|
|
65
|
+
audit, and a public snapshot dry-run, then prints a readiness report. It is
|
|
66
|
+
intentionally local-only and does not trigger GitHub Actions or tunnel
|
|
67
|
+
providers. If the report is `needs_attention` only because
|
|
68
|
+
`preserved-history-audit` found private dogfooding fingerprints, do not change
|
|
69
|
+
the existing GitHub repository to public visibility; publish a fresh
|
|
70
|
+
`public:mirror` instead.
|
|
71
|
+
|
|
72
|
+
For that fresh public mirror release path, run:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm run public:mirror -- --remote <github-owner>/<public-repo>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This accepts the preserved-history warning only for a detached snapshot release;
|
|
79
|
+
it does not mean the private repository can be made public with existing
|
|
80
|
+
history. `public:ready` and `alpha:snapshot-check` remain lower-level aliases
|
|
81
|
+
for checking readiness without writing the mirror.
|
|
82
|
+
|
|
83
|
+
Before announcing a public alpha, capture one external MCP client/tunnel pass as
|
|
84
|
+
machine-readable evidence:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run alpha:evidence -- preflight
|
|
88
|
+
npm run alpha:evidence -- smoke --redaction-confirmed
|
|
89
|
+
npm run alpha:check -- --require-evidence
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The generated evidence file is gitignored and should remain local. It records
|
|
93
|
+
which client, exposure path, generic MCP tool flow, MCP-only public surface, and
|
|
94
|
+
history review were tested. It must not contain owner tokens, API keys, bearer
|
|
95
|
+
headers, screenshots, or private file contents. The check rejects placeholder
|
|
96
|
+
client names, placeholder tunnel targets, non-HTTPS public URLs, non-`/mcp` MCP
|
|
97
|
+
paths, and stale evidence. Use `alpha:evidence smoke` to create or refresh the
|
|
98
|
+
evidence file and mark the full external smoke pass without hand-editing JSON;
|
|
99
|
+
it auto-detects exposure, tunnel target, and scope from local preflight state
|
|
100
|
+
when possible. Pass explicit `--client`, `--exposure`, `--tunnel-or-url`, or
|
|
101
|
+
`--scope` only when auto-detection cannot infer the tested target. `smoke` can
|
|
102
|
+
refresh an existing Computer Linker alpha evidence file, but still requires
|
|
103
|
+
`--force` before replacing unrelated files. Use `alpha:evidence init`,
|
|
104
|
+
`record-smoke`, or `record` only when you want to split the steps or keep
|
|
105
|
+
separate notes per check. `smoke`, `init`, `record-smoke`, and `record` refuse
|
|
106
|
+
common secret-shaped values before writing the evidence file. Use
|
|
107
|
+
`alpha:evidence preflight` before recording evidence to inspect local
|
|
108
|
+
config, audit history, and tunnel runtime state for missing external smoke
|
|
109
|
+
signals and print a read-only prompt to paste into the external MCP client.
|
|
110
|
+
When a previous attempt already completed some calls, `nextExternalClientPrompt`
|
|
111
|
+
narrows the prompt to the missing operation. It is a preflight only; it does not
|
|
112
|
+
replace manual redaction and client-instruction confirmation. `recordCommand`
|
|
113
|
+
shows the short evidence command to run when the preflight no longer fails. Use
|
|
114
|
+
[alpha-evidence.example.json](alpha-evidence.example.json) for the schema.
|
|
115
|
+
The public release audit blocks `.computer-linker-alpha-evidence.json` if it
|
|
116
|
+
is ever tracked or packed; only the example schema belongs in the public repo.
|
|
117
|
+
|
|
118
|
+
Before publishing the detached public mirror, run the final local release
|
|
119
|
+
preflight:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm run public:release-ready
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This is the release-oriented alpha readiness gate. It requires fresh external
|
|
126
|
+
MCP evidence and a dated `CHANGELOG.md` heading for the current package version,
|
|
127
|
+
so it reports both final blockers in one place before `public:mirror`.
|
|
128
|
+
|
|
129
|
+
This adds the public-release audit: packed-file inspection, tracked and
|
|
130
|
+
non-ignored untracked file secret-shape scanning, production `npm audit`,
|
|
131
|
+
dependency license allowlist checks, and a high-risk Git history secret scan.
|
|
132
|
+
|
|
133
|
+
Before changing the current GitHub repository to public visibility while
|
|
134
|
+
preserving its Git history, run the stricter one-command gate:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm run public:repo-ready
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
If strict history fails because private dogfooding commits contain local
|
|
141
|
+
fingerprints, do not rewrite `main` unless every collaborator agrees. Publish a
|
|
142
|
+
fresh public mirror instead:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run public:mirror -- --remote <github-owner>/<public-repo>
|
|
146
|
+
cd ../computer-linker-public
|
|
147
|
+
git push -u origin main --follow-tags
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`public:mirror` runs the public readiness gate once, then calls the lower-level
|
|
151
|
+
`public:snapshot` path with the already-checked commit. `public:snapshot`
|
|
152
|
+
refuses a dirty worktree, copies the committed `HEAD` tree only, and creates a
|
|
153
|
+
new single-commit repository using your configured Git author identity. The
|
|
154
|
+
generated commit bypasses local Git hooks because the script performs its own
|
|
155
|
+
snapshot verification. It verifies that the generated repository has exactly
|
|
156
|
+
one clean commit, a `v<package.version>` tag pointing at that commit, and no
|
|
157
|
+
strict-history local fingerprints before printing push instructions. Use
|
|
158
|
+
`--remote <github-owner>/<public-repo>` for publishable mirrors; the shorthand is
|
|
159
|
+
normalized to `https://github.com/<github-owner>/<public-repo>.git`, and full
|
|
160
|
+
GitHub URLs also work. The remote is added during snapshot creation, and when
|
|
161
|
+
that remote is a GitHub repository, the snapshot's `package.json` repository,
|
|
162
|
+
issue, and homepage links are rewritten to the public repository. Public-facing
|
|
163
|
+
GitHub links in files such as issue-template config and schema ids are also
|
|
164
|
+
rewritten. Real publishable mirrors require the matching changelog heading to
|
|
165
|
+
be dated instead of `Unreleased`; remote dry-runs print whether the real run
|
|
166
|
+
would be blocked. The default output directory is `../computer-linker-public`; pass
|
|
167
|
+
`--output <path>` only when you need a different disposable mirror directory.
|
|
168
|
+
When the default directory already contains a clean one-commit snapshot created
|
|
169
|
+
by Computer Linker, `public:snapshot` replaces it automatically so the short
|
|
170
|
+
publish command is repeatable.
|
|
171
|
+
Without `--remote`, the output is a local verification snapshot and the package
|
|
172
|
+
metadata remains unchanged; do not push that verification-only mirror. Use
|
|
173
|
+
`--force` only when replacing a non-default disposable output directory or a
|
|
174
|
+
manually changed generated mirror. `--allow-dirty` is accepted only with `--dry-run`; committed `HEAD`
|
|
175
|
+
must be clean before creating a real public snapshot. Snapshot commits omit the
|
|
176
|
+
private source commit reference by default; use `--include-source-ref` only when
|
|
177
|
+
release traceability is more important than keeping the public mirror detached
|
|
178
|
+
from the private dogfooding history.
|
|
179
|
+
|
|
180
|
+
Use `npm run public:ready` and `npm run public:snapshot` directly only when
|
|
181
|
+
debugging a failed release gate.
|
|
182
|
+
|
|
183
|
+
`computer-linker doctor` and `/api/v1/control` with `action: "doctor"` also
|
|
184
|
+
return `releaseReadiness`. The release is blocked when that object has
|
|
185
|
+
`status: "blocked"` or non-empty `blockingReasons`.
|
|
186
|
+
`computer-linker config validate` exposes the same config, security, and
|
|
187
|
+
release-readiness subset for release scripts and exits non-zero when the status
|
|
188
|
+
is blocked.
|
|
189
|
+
|
|
190
|
+
## Required Before Tagging
|
|
191
|
+
|
|
192
|
+
- `npm run product:check` passes locally, and the manual Windows/Node 22 CI gate
|
|
193
|
+
passes when Actions budget is available.
|
|
194
|
+
- `npm run alpha:check` passes locally before sharing an alpha build or public
|
|
195
|
+
snapshot; a `preserved-history-audit` warning is acceptable only when the
|
|
196
|
+
release will use `public:mirror` instead of preserving private repo history.
|
|
197
|
+
- `npm run public:mirror -- --dry-run --remote <github-owner>/<public-repo>`
|
|
198
|
+
passes before publishing from a fresh public mirror.
|
|
199
|
+
- `npm run alpha:check -- --require-evidence` passes before announcing a public
|
|
200
|
+
alpha outside private dogfooding.
|
|
201
|
+
- Broader OS or Node coverage is run manually before a wider release when the
|
|
202
|
+
Actions budget allows it.
|
|
203
|
+
- `npm run public:check` passes before publishing a package artifact.
|
|
204
|
+
- `npm run public:repo-ready` passes before changing the existing GitHub
|
|
205
|
+
repository to public visibility with preserved history. If it fails only on
|
|
206
|
+
local history fingerprints, publish from `public:mirror` instead.
|
|
207
|
+
- `computer-linker doctor --json` reports no `releaseReadiness.blockingReasons`
|
|
208
|
+
on the target machine.
|
|
209
|
+
- `computer-linker config validate` exits successfully on the target machine.
|
|
210
|
+
- At least one workspace scope is configured and its path exists.
|
|
211
|
+
- HTTP exposure has an owner token before any tunnel is started.
|
|
212
|
+
- Shell or Codex scopes have an explicit command allowlist when used outside
|
|
213
|
+
local dogfooding.
|
|
214
|
+
- README, product spec, architecture docs, changelog, security policy, and
|
|
215
|
+
`computer_operation` schema match the shipped contract.
|
|
216
|
+
- Tagged releases use `v<package.version>` and replace the current changelog
|
|
217
|
+
heading's `Unreleased` suffix with a release date before pushing the tag.
|
|
218
|
+
Fresh public mirrors create this tag automatically; push it with
|
|
219
|
+
`--follow-tags`.
|
|
220
|
+
- Manual `npm publish` is guarded by `prepublishOnly`, which requires a clean
|
|
221
|
+
worktree, `v<package.version>` on `HEAD`, a dated changelog heading,
|
|
222
|
+
`npm run public:check`, and strict public-history audit before npm prepares
|
|
223
|
+
the package.
|
|
224
|
+
|
|
225
|
+
## Security Review
|
|
226
|
+
|
|
227
|
+
For each shell or Codex-enabled scope, verify:
|
|
228
|
+
|
|
229
|
+
- The scope points at a trusted project directory.
|
|
230
|
+
- `allowedCommands` is narrow enough for expected workflows.
|
|
231
|
+
- `deniedCommands` blocks known destructive local patterns for that team.
|
|
232
|
+
- `maxRuntimeSeconds` and `maxOutputBytes` are finite.
|
|
233
|
+
- The user understands that shell and Codex are cwd-bound execution, not an OS
|
|
234
|
+
filesystem sandbox.
|
|
235
|
+
|
|
236
|
+
## Smoke Commands
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
computer-linker init
|
|
240
|
+
computer-linker self-test
|
|
241
|
+
computer-linker status
|
|
242
|
+
computer-linker status --details
|
|
243
|
+
computer-linker status --json
|
|
244
|
+
computer-linker doctor
|
|
245
|
+
computer-linker doctor --json
|
|
246
|
+
computer-linker config validate
|
|
247
|
+
computer-linker config policy app --json
|
|
248
|
+
computer-linker client setup
|
|
249
|
+
computer-linker client setup --details
|
|
250
|
+
computer-linker client setup --show-token
|
|
251
|
+
computer-linker client setup --json
|
|
252
|
+
computer-linker client smoke --url http://127.0.0.1:3939/mcp --allow-http
|
|
253
|
+
computer-linker client chatgpt verify --mode coding
|
|
254
|
+
npm run alpha:evidence -- check
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
For package validation from a checkout:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
npm ci
|
|
261
|
+
npm run release:validate
|
|
262
|
+
npm run product:check
|
|
263
|
+
npm run public:audit
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
For preserved-history GitHub repository publication, replace the last two
|
|
267
|
+
commands with:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npm run public:repo-ready
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The manual GitHub Actions `Release Package` workflow runs `public:repo-ready`
|
|
274
|
+
and uploads the resulting `computer-linker-*.tgz` artifact for final
|
|
275
|
+
inspection. It must be launched manually from the `v<package.version>` tag in a
|
|
276
|
+
repo whose preserved history is safe for public packaging, such as the
|
|
277
|
+
`--remote` public snapshot. The workflow also rejects an `Unreleased` changelog
|
|
278
|
+
heading before spending time on the full public gate.
|
|
279
|
+
The default CI workflow is also manual and cost-capped: run it from
|
|
280
|
+
`workflow_dispatch` only when Actions budget is available. It should stay on
|
|
281
|
+
`windows-latest` with Node 22 and run `npm run product:check`; run broader OS or
|
|
282
|
+
Node coverage manually only for a wider release. `npm run release:validate`
|
|
283
|
+
rejects automatic triggers, matrix jobs, non-Windows runners, and extra Node
|
|
284
|
+
versions in the default workflows.
|
|
285
|
+
Use [manual-test-plan.md](manual-test-plan.md) for the local dogfooding pass.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Service Mode
|
|
2
|
+
|
|
3
|
+
Computer Linker can run as a background service on Linux, macOS, and Windows.
|
|
4
|
+
The default service flow is conservative: preview with `--dry-run`, then apply
|
|
5
|
+
install/uninstall with `--yes`.
|
|
6
|
+
|
|
7
|
+
Generate a profile for the current platform:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
computer-linker service profile
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Generate a specific platform manifest:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
computer-linker service profile --platform linux --format manifest
|
|
17
|
+
computer-linker service profile --platform macos --format manifest
|
|
18
|
+
computer-linker service profile --platform windows --format manifest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Write a complete service bundle:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
computer-linker service profile --platform linux --output-dir ./service-profile
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The bundle contains:
|
|
28
|
+
|
|
29
|
+
- `service-profile.json`
|
|
30
|
+
- the native service manifest (`.service`, `.plist`, or `.ps1`)
|
|
31
|
+
- `install-service.sh` or `install-service.ps1`
|
|
32
|
+
- `uninstall-service.sh` or `uninstall-service.ps1`
|
|
33
|
+
|
|
34
|
+
Check what Computer Linker expects on this machine:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
computer-linker service status
|
|
38
|
+
computer-linker service status --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Preview install or uninstall commands without changing the OS:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
computer-linker service install --dry-run
|
|
45
|
+
computer-linker service install --dry-run --json
|
|
46
|
+
computer-linker service uninstall --dry-run
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Install or remove the service on the current platform:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
computer-linker service install --yes
|
|
53
|
+
computer-linker service start
|
|
54
|
+
computer-linker service status
|
|
55
|
+
computer-linker service logs
|
|
56
|
+
computer-linker service stop
|
|
57
|
+
computer-linker service uninstall --yes
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`install --yes` writes the service bundle under the Computer Linker config
|
|
61
|
+
directory by default, then runs the generated install script. Pass
|
|
62
|
+
`--output-dir ./service-profile` when you want the bundle somewhere else.
|
|
63
|
+
`service start` and `service stop` control the already-installed service on the
|
|
64
|
+
current platform. Use `--dry-run` for cross-platform plans such as
|
|
65
|
+
`--platform linux` while working on Windows.
|
|
66
|
+
|
|
67
|
+
Before installing:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
computer-linker init
|
|
71
|
+
computer-linker doctor
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`doctor` includes a machine-readable `startup` block with foreground start,
|
|
75
|
+
HTTP, stdio, and persistent service startup modes. It also includes the current
|
|
76
|
+
platform service status commands, profile command, bundle command, and install
|
|
77
|
+
dry-run command, so a setup script or MCP client can discover the right service
|
|
78
|
+
workflow without parsing this document.
|
|
79
|
+
|
|
80
|
+
The generated service runs:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
computer-linker serve --transport http
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
It uses `COMPUTER_LINKER_CONFIG_DIR` so the service reads the same bounded
|
|
87
|
+
workspace config as the interactive CLI. Host, port, owner token, public URL,
|
|
88
|
+
and workspace permissions stay in `config.json`.
|
|
89
|
+
|
|
90
|
+
Platform notes:
|
|
91
|
+
|
|
92
|
+
- Linux uses `systemd`.
|
|
93
|
+
- macOS uses a per-user `launchd` agent in `~/Library/LaunchAgents`.
|
|
94
|
+
- Windows uses `sc.exe`; install/uninstall require an elevated PowerShell
|
|
95
|
+
prompt. Generated Windows services write `service.out.log` and
|
|
96
|
+
`service.err.log` under the Computer Linker config directory.
|
|
97
|
+
|
|
98
|
+
Keep the HTTP service loopback-only unless it is protected by Tailscale,
|
|
99
|
+
Cloudflare Access, or equivalent network controls.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
+
|
|
5
|
+
const url = new URL(
|
|
6
|
+
process.env.COMPUTER_LINKER_MCP_URL ??
|
|
7
|
+
process.env.WORKSPACE_LINKER_MCP_URL ??
|
|
8
|
+
process.argv[2] ??
|
|
9
|
+
"http://127.0.0.1:3939/mcp",
|
|
10
|
+
);
|
|
11
|
+
const token =
|
|
12
|
+
process.env.COMPUTER_LINKER_TOKEN ??
|
|
13
|
+
process.env.COMPUTER_LINKER_OWNER_TOKEN ??
|
|
14
|
+
process.env.WORKSPACE_LINKER_TOKEN ??
|
|
15
|
+
process.env.WORKSPACE_LINKER_OWNER_TOKEN ??
|
|
16
|
+
process.argv[3];
|
|
17
|
+
|
|
18
|
+
const client = new Client({
|
|
19
|
+
name: "computer-linker-minimal-client",
|
|
20
|
+
version: "0.1.0",
|
|
21
|
+
});
|
|
22
|
+
const transport = new StreamableHTTPClientTransport(url, {
|
|
23
|
+
requestInit: {
|
|
24
|
+
headers: token ? { authorization: `Bearer ${token}` } : undefined,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await client.connect(transport);
|
|
30
|
+
|
|
31
|
+
const tools = await client.listTools();
|
|
32
|
+
const toolNames = tools.tools.map((tool) => tool.name);
|
|
33
|
+
console.log(`tools: ${toolNames.join(", ")}`);
|
|
34
|
+
|
|
35
|
+
for (const requiredTool of ["get_computer_info", "computer_operation", "get_operation_history"]) {
|
|
36
|
+
if (!toolNames.includes(requiredTool)) {
|
|
37
|
+
throw new Error(`Missing required tool: ${requiredTool}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const computerInfo = toolData(await client.callTool({
|
|
42
|
+
name: "get_computer_info",
|
|
43
|
+
arguments: {},
|
|
44
|
+
}));
|
|
45
|
+
const scope = readableScope(computerInfo);
|
|
46
|
+
if (!scope) {
|
|
47
|
+
throw new Error("No readable scope returned by get_computer_info.");
|
|
48
|
+
}
|
|
49
|
+
console.log(`scope: ${scope}`);
|
|
50
|
+
|
|
51
|
+
const operation = toolData(await client.callTool({
|
|
52
|
+
name: "computer_operation",
|
|
53
|
+
arguments: {
|
|
54
|
+
scope,
|
|
55
|
+
op: "file.list",
|
|
56
|
+
target: ".",
|
|
57
|
+
input: {},
|
|
58
|
+
options: { maxEntries: 5 },
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
if (operation?.ok !== true) {
|
|
62
|
+
throw new Error(`computer_operation file.list failed: ${operation?.error?.message ?? "unknown error"}`);
|
|
63
|
+
}
|
|
64
|
+
console.log("file.list: ok");
|
|
65
|
+
|
|
66
|
+
const history = toolData(await client.callTool({
|
|
67
|
+
name: "get_operation_history",
|
|
68
|
+
arguments: {
|
|
69
|
+
view: "last",
|
|
70
|
+
limit: 5,
|
|
71
|
+
},
|
|
72
|
+
}));
|
|
73
|
+
console.log(`history: ${history?.view ?? "unknown"}`);
|
|
74
|
+
} finally {
|
|
75
|
+
await closeClient();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function toolData(result) {
|
|
79
|
+
if (result?.structuredContent && typeof result.structuredContent === "object") {
|
|
80
|
+
return result.structuredContent;
|
|
81
|
+
}
|
|
82
|
+
const text = result?.content?.find((item) => item.type === "text" && typeof item.text === "string")?.text;
|
|
83
|
+
if (!text) return undefined;
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(text);
|
|
86
|
+
} catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readableScope(computerInfo) {
|
|
92
|
+
return computerInfo?.scopes?.find((scope) => (
|
|
93
|
+
typeof scope.id === "string" &&
|
|
94
|
+
(
|
|
95
|
+
scope.permissions?.read === true ||
|
|
96
|
+
scope.allowedOperations?.includes("file.list") ||
|
|
97
|
+
scope.allowedOperations?.includes("read") ||
|
|
98
|
+
scope.allowedOperations?.includes("search_text")
|
|
99
|
+
)
|
|
100
|
+
))?.id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function closeClient() {
|
|
104
|
+
try {
|
|
105
|
+
if (transport.sessionId) await transport.terminateSession();
|
|
106
|
+
} catch {
|
|
107
|
+
// Best effort.
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
await client.close();
|
|
111
|
+
} catch {
|
|
112
|
+
// Best effort.
|
|
113
|
+
}
|
|
114
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@easonwumac/computer-linker",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "One computer, one permissioned MCP linker for local workspaces.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/client.js",
|
|
7
|
+
"types": "dist/client.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"docs",
|
|
11
|
+
"examples",
|
|
12
|
+
"README.md",
|
|
13
|
+
"CHANGELOG.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"SECURITY.md"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"mcp-server",
|
|
20
|
+
"workspace",
|
|
21
|
+
"codex",
|
|
22
|
+
"local-first",
|
|
23
|
+
"automation"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/easonwumac/computer-linker.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/easonwumac/computer-linker/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/easonwumac/computer-linker#readme",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/client.d.ts",
|
|
36
|
+
"import": "./dist/client.js"
|
|
37
|
+
},
|
|
38
|
+
"./client": {
|
|
39
|
+
"types": "./dist/client.d.ts",
|
|
40
|
+
"import": "./dist/client.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"bin": {
|
|
44
|
+
"computer-linker": "dist/cli.js"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
|
|
48
|
+
"alpha:check": "node scripts/alpha-readiness-report.mjs",
|
|
49
|
+
"alpha:evidence": "node scripts/alpha-evidence.mjs",
|
|
50
|
+
"alpha:snapshot-check": "node scripts/alpha-readiness-report.mjs --accept-public-snapshot",
|
|
51
|
+
"dev": "tsx src/cli.ts",
|
|
52
|
+
"pack:check": "npm run build && npm run pack:smoke",
|
|
53
|
+
"pack:smoke": "node scripts/package-smoke.mjs",
|
|
54
|
+
"prepublishOnly": "npm run publish:guard",
|
|
55
|
+
"product:check": "npm run release:validate && npm run typecheck && npm test && npm run build && npm run pack:smoke",
|
|
56
|
+
"publish:guard": "node scripts/publish-guard.mjs",
|
|
57
|
+
"public:audit": "node scripts/public-release-audit.mjs",
|
|
58
|
+
"public:check": "npm run product:check && npm run public:audit",
|
|
59
|
+
"public:mirror": "node scripts/create-public-mirror.mjs",
|
|
60
|
+
"public:ready": "node scripts/alpha-readiness-report.mjs --accept-public-snapshot",
|
|
61
|
+
"public:release-ready": "node scripts/alpha-readiness-report.mjs --accept-public-snapshot --require-evidence --require-dated-changelog",
|
|
62
|
+
"public:repo-ready": "npm run product:check && npm run public:audit -- --strict-history",
|
|
63
|
+
"public:snapshot": "node scripts/create-public-snapshot.mjs",
|
|
64
|
+
"release:check": "node scripts/release-npm.mjs --check",
|
|
65
|
+
"release:dry-run": "node scripts/release-npm.mjs --dry-run",
|
|
66
|
+
"release:publish": "node scripts/release-npm.mjs --publish",
|
|
67
|
+
"release:validate": "node scripts/release-validate.mjs",
|
|
68
|
+
"start": "node dist/cli.js start",
|
|
69
|
+
"test": "node scripts/run-tests.mjs",
|
|
70
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=20.12"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
77
|
+
"express": "^5.2.1",
|
|
78
|
+
"zod": "^4.4.3"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@types/express": "^5.0.6",
|
|
82
|
+
"@types/node": "^25.9.1",
|
|
83
|
+
"tsx": "^4.22.3",
|
|
84
|
+
"typescript": "^6.0.3"
|
|
85
|
+
},
|
|
86
|
+
"license": "MIT"
|
|
87
|
+
}
|