opencode-lookout 0.0.0-stage → 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 +75 -0
- package/CODE_OF_CONDUCT.md +20 -0
- package/CONTRIBUTING.md +95 -0
- package/GOVERNANCE.md +20 -0
- package/LICENSE +21 -0
- package/README.md +239 -2
- package/SECURITY.md +35 -0
- package/SUPPORT.md +20 -0
- package/bin/lookout.mjs +99 -0
- package/dist/tui.js +534 -0
- package/docs/ARCHITECTURE.md +124 -0
- package/docs/RELEASE_GATES_PLAN.md +67 -0
- package/docs/RELEASING.md +171 -0
- package/docs/THREAT_MODEL.md +176 -0
- package/docs/VALIDATION.md +99 -0
- package/package.json +103 -4
- package/src/catalog.mjs +278 -0
- package/src/compatibility.mjs +17 -0
- package/src/config.mjs +250 -0
- package/src/controller.mjs +199 -0
- package/src/plugin-description.mjs +90 -0
- package/src/rpc.ts +46 -0
- package/src/server.ts +159 -0
- package/src/tui.tsx +522 -0
- package/src/types.ts +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.2 — 2026-11-08
|
|
4
|
+
|
|
5
|
+
- Add an allowlisted npm artifact and a `lookout` executable for stable-prefix
|
|
6
|
+
installs. Correct executable detection through npm's symlink; preserve the
|
|
7
|
+
existing checkout invocation and path-owned recovery markers.
|
|
8
|
+
- Precompile the Solid TUI for loading inside `node_modules`; check generated
|
|
9
|
+
output against source. Declare the host SDK/UI runtime as optional peers rather
|
|
10
|
+
than installing a second SDK graph with consumer-unprotected dependencies.
|
|
11
|
+
- Test production-installed tarballs, offline failure/recovery, consumer dependency
|
|
12
|
+
audits, and the real server/TUI matrix. Bind Docker receipts to tarball integrity.
|
|
13
|
+
- Add a manual OIDC publication workflow requiring the exact approved signed tag,
|
|
14
|
+
published GitHub release, matching main tree, fresh full hosted checks and fresh
|
|
15
|
+
local release gates. The maintainer approved a one-time authenticated bootstrap
|
|
16
|
+
for this first npm version; it has no GitHub OIDC provenance. Future versions
|
|
17
|
+
use trusted publishing after package setup.
|
|
18
|
+
- Match the full catalog heading after cancelling a terminal preview, rather than
|
|
19
|
+
requiring a contiguous placeholder in changed-cell renderer output. Preserve
|
|
20
|
+
cancellation, confirmed mutation, and offline recovery assertions.
|
|
21
|
+
|
|
22
|
+
## 0.1.1 — 2026-11-05
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Remove an unused fixture session-response dump reported by full CodeQL analysis.
|
|
27
|
+
The isolated terminal tests now assert that the response is not saved.
|
|
28
|
+
- Require full CodeQL scans on candidate branch pushes. PR-only scans use separate
|
|
29
|
+
check names and cannot satisfy the required full-scan checks. Release procedure
|
|
30
|
+
also verifies post-merge checks before publishing.
|
|
31
|
+
- Supersede the withdrawn 0.1.0 release, whose PR analysis passed but post-merge
|
|
32
|
+
full analysis reported the fixture finding. The 0.1.0 tag remains immutable.
|
|
33
|
+
|
|
34
|
+
## 0.1.0 — 2026-11-05
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Native-style Lookout sidebar for agents, skills, plugins, commands, providers,
|
|
39
|
+
and models, with search, category/state filters, details, and pinned entries.
|
|
40
|
+
- Confirmed, exact-ID restrictions for locally controllable skills and plugins.
|
|
41
|
+
- Independent load, unload, status, and restore commands.
|
|
42
|
+
- Plugin-file descriptions in the sidebar, with `missing description` when
|
|
43
|
+
supported metadata is absent; plugin code is not executed to read metadata.
|
|
44
|
+
- Public MIT-licensed repository, contributor guidance, security policy, CI,
|
|
45
|
+
issue templates, and dependency-update configuration.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- Standardized product and machine-facing names on Lookout: `opencode-lookout`
|
|
50
|
+
plugin/RPC ID, `/lookout`, `bin/lookout.mjs`, and `lookout:` ownership markers.
|
|
51
|
+
This is a deliberate breaking rename, without legacy aliases. Restore and
|
|
52
|
+
unload an older installation using its own CLI before reinstalling.
|
|
53
|
+
- Enabled strict checked JavaScript, typed catalog projections, and blocking lint.
|
|
54
|
+
- Pinned patched glob/minimatch overrides for all affected dependency versions.
|
|
55
|
+
- Aligned development Node declarations with Node 26 using `@types/node` 26.6.6.
|
|
56
|
+
Production-only offline CLI tests retain Node 22 and 24 coverage.
|
|
57
|
+
- Added fail-closed OSS security checks, source-bound release evidence, dynamic
|
|
58
|
+
Docker compatibility tests, and portable OpenCode/Copilot/VS Code guidance.
|
|
59
|
+
- Matched native semantic status-dot colors and removed duplicate MCP browsing.
|
|
60
|
+
Native MCP controls and existing extension-owned restrictions are unchanged.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- Validate opened configuration descriptors and bound reads against file growth,
|
|
65
|
+
hard-link changes, and stale permission metadata; reject FIFOs and final symlinks.
|
|
66
|
+
- Install runtime-test dependencies with npm 11.19.1 to preserve patched overrides
|
|
67
|
+
that npm 10 misresolves. Encode complete registry URL components.
|
|
68
|
+
- Extract checksum-verified rules from memory into fresh directories and distinguish
|
|
69
|
+
informational CodeQL diagnostics from findings without severity waivers.
|
|
70
|
+
|
|
71
|
+
### Validation
|
|
72
|
+
|
|
73
|
+
- OpenCode 2.2.5 server lifecycle and reversible-control integration tests.
|
|
74
|
+
- Carbonfox wide-terminal and Tokyonight narrow-terminal keyboard workflows.
|
|
75
|
+
- See [Validation](docs/VALIDATION.md) for exact evidence and known limits.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
Help make this project useful and welcoming. Treat people with respect regardless
|
|
4
|
+
of background, identity, experience, or technical ability. Critique ideas and code,
|
|
5
|
+
not people. Accept constructive feedback and acknowledge mistakes.
|
|
6
|
+
|
|
7
|
+
Harassment, threats, discriminatory language, sexualized content, personal attacks,
|
|
8
|
+
and publishing someone else's private information are not acceptable. Do not use
|
|
9
|
+
project channels for spam or repeated unwanted contact.
|
|
10
|
+
|
|
11
|
+
This policy applies to issues, pull requests, discussions, and other spaces where
|
|
12
|
+
someone represents the project. The maintainer may edit or remove harmful content,
|
|
13
|
+
warn participants, restrict participation, or ban accounts according to severity
|
|
14
|
+
and repeated behavior. Maintainers are also bound by this policy.
|
|
15
|
+
|
|
16
|
+
Report concerns to [@sodejm](https://github.com/sodejm). For sensitive incidents,
|
|
17
|
+
request a private contact method in a minimal issue without naming affected people
|
|
18
|
+
or describing the incident publicly. Share details only after a private channel
|
|
19
|
+
is established. The maintainer will limit disclosure to what is needed to address
|
|
20
|
+
the concern. No response-time guarantee is made.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Contributing to OpenCode Lookout
|
|
2
|
+
|
|
3
|
+
Start with a focused issue for substantial behavior or API changes. Bug fixes,
|
|
4
|
+
tests, and documentation improvements can go directly to a pull request. Keep
|
|
5
|
+
unrelated cleanup separate. Contributions are licensed under [MIT](LICENSE).
|
|
6
|
+
|
|
7
|
+
## Set up
|
|
8
|
+
|
|
9
|
+
Use Node.js 26.4.0+ and npm for development, as required by OpenTUI 0.8.0.
|
|
10
|
+
Use npm 11 or later to install the lockfile. npm 10 misresolves the patched
|
|
11
|
+
override graph and rejects `npm ci`; do not remove the overrides to work around it.
|
|
12
|
+
Runtime CI installs with npm 11.19.1 while still running tests on Node 22 and 24.
|
|
13
|
+
`make check` also installs and audits the packed artifact in a temporary consumer
|
|
14
|
+
directory. It requires registry access and fails closed on registry errors.
|
|
15
|
+
After editing `src/tui.tsx`, run `npm run build` and review `dist/tui.js` alongside
|
|
16
|
+
the source. `build:check` requires byte-identical output from the pinned compiler.
|
|
17
|
+
The generated TUI is committed so production-only runtime tests can pack the
|
|
18
|
+
same artifact without installing build tools. It remains security-scanned.
|
|
19
|
+
Development uses Node 26 declarations. Do not assume a type-checked Node API is
|
|
20
|
+
available to the offline CLI on Node 22; its production-only runtime tests remain required.
|
|
21
|
+
The offline CLI retains Node.js 22+ support with production
|
|
22
|
+
dependencies only. OpenCode 2.2.5 is needed only for live integration
|
|
23
|
+
tests. The optional terminal harness needs `/usr/bin/expect` (macOS or Linux).
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
git clone https://github.com/sodejm/opencode-lookout.git
|
|
27
|
+
cd opencode-lookout
|
|
28
|
+
npm ci --ignore-scripts
|
|
29
|
+
make check
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Do not load an unreviewed contribution into your personal OpenCode environment.
|
|
33
|
+
Use `npm run test:integration` or `npm run test:tui`: these start an isolated server
|
|
34
|
+
with temporary HOME/XDG directories and deny provider and tool use. See
|
|
35
|
+
[Validation](docs/VALIDATION.md) for the runtime matrix and limitations.
|
|
36
|
+
|
|
37
|
+
## Make a change
|
|
38
|
+
|
|
39
|
+
1. Create a focused branch. Reproduce a behavioral bug with a test when practical.
|
|
40
|
+
2. Preserve the plugin ID, RPC contract, configuration markers, and offline
|
|
41
|
+
recovery commands unless a migration has been explicitly agreed.
|
|
42
|
+
3. Use typed TypeScript contracts and JSDoc for exported JavaScript behavior.
|
|
43
|
+
Explain non-obvious constraints and failure paths, not routine assignments.
|
|
44
|
+
4. Update affected user and architecture docs. For security-sensitive changes,
|
|
45
|
+
update the [threat model](docs/THREAT_MODEL.md) and test failure/recovery paths.
|
|
46
|
+
5. Run `make check`. For UI or config changes, also run both terminal cases in the
|
|
47
|
+
validation guide. Do not describe unavailable checks as passing.
|
|
48
|
+
6. Review the diff for credentials, personal paths, transcripts, and unrelated
|
|
49
|
+
edits. Never commit retained test fixtures or global OpenCode configuration.
|
|
50
|
+
7. Submit a PR with the behavior change, evidence, compatibility impact, and
|
|
51
|
+
recovery procedure. A maintainer reviews the exact revision before merging.
|
|
52
|
+
|
|
53
|
+
Format source with the pinned Prettier version:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npm exec -- prettier --write 'src/**/*.{mjs,ts,tsx}' 'test/**/*.mjs' 'bin/*.mjs' 'scripts/*.mjs' '*.json'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Dependencies and CI
|
|
60
|
+
|
|
61
|
+
Keep the SDK and OpenTUI versions aligned with the tested OpenCode release. Commit
|
|
62
|
+
`package-lock.json`; install with scripts disabled. Explain new dependencies and
|
|
63
|
+
their trust implications. CI checks quality on Node.js 26 and runtime tests with
|
|
64
|
+
production dependencies on Node.js 22 and 24. The Docker job
|
|
65
|
+
exercises real OpenCode loading and terminal workflows; it does not prove provider
|
|
66
|
+
authentication or pixel-level rendering. All security severities block release.
|
|
67
|
+
|
|
68
|
+
GitHub Actions use pinned revisions and read-only repository permissions, except
|
|
69
|
+
the existing CodeQL job's `security-events: write` permission to upload scan
|
|
70
|
+
results and the manual npm job's `id-token: write` for trusted publishing.
|
|
71
|
+
Do not add secret-bearing PR jobs, `pull_request_target` execution of
|
|
72
|
+
contributor code, or automatic publishing. AI-assisted contributions have the
|
|
73
|
+
same testing, attribution, security, and human-review requirements as other work.
|
|
74
|
+
|
|
75
|
+
## Agent and editor development
|
|
76
|
+
|
|
77
|
+
OpenCode reads `opencode.json` and the shared `AGENTS.md`/contributor guidance.
|
|
78
|
+
Copilot's repository instructions point to the same contract. Neither adapter
|
|
79
|
+
selects models, grants permissions, loads this plugin, or installs MCP servers.
|
|
80
|
+
|
|
81
|
+
Open `opencode-lookout.code-workspace` in VS Code, review Workspace Trust, and run
|
|
82
|
+
**Lookout: install** yourself. Accepting recommended extensions is optional.
|
|
83
|
+
Tasks cover checks, lint, tests, Docker compatibility, security, and release
|
|
84
|
+
assessment. Tasks do not run on workspace open. Use the workspace TypeScript
|
|
85
|
+
version after installation. Terminal tests need Linux/macOS; use Docker on other
|
|
86
|
+
hosts. No editor task loads the plugin into personal configuration.
|
|
87
|
+
|
|
88
|
+
## Dependency remediation
|
|
89
|
+
|
|
90
|
+
The lockfile overrides vulnerable `glob` 9–14.0.1 with 14.0.3 and `minimatch`
|
|
91
|
+
7–11.0.0 with 11.0.4. These major-version overrides address
|
|
92
|
+
GHSA-3g3m-2fjr-6pw4 and GHSA-g3hv-75wg-m249; they are not severity waivers.
|
|
93
|
+
Older unaffected majors are not replaced. `make check` and actual server/TUI
|
|
94
|
+
integration must pass after each override change. Remove an override only when
|
|
95
|
+
the upstream graph resolves patched versions and the full checks still pass.
|
package/GOVERNANCE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Governance
|
|
2
|
+
|
|
3
|
+
OpenCode Lookout is maintained by [@sodejm](https://github.com/sodejm). It is an
|
|
4
|
+
independent community extension. The maintainer owns scope, compatibility,
|
|
5
|
+
dependency, security, and release decisions.
|
|
6
|
+
|
|
7
|
+
Use issues for proposals and pull requests for reviewable changes. Explain the
|
|
8
|
+
problem and relevant trade-offs before proposing broad API or configuration
|
|
9
|
+
changes. The maintainer makes the final decision and records material decisions
|
|
10
|
+
in the issue, PR, or affected architecture documentation.
|
|
11
|
+
|
|
12
|
+
The maintainer may review and merge their own contributions; a second reviewer is
|
|
13
|
+
not required. Automated checks and AI reviews supplement human review, not replace
|
|
14
|
+
it. Human review must cover the exact revision and its failure/recovery behavior.
|
|
15
|
+
|
|
16
|
+
The repository is public, but npm publication is deliberately disabled. There is
|
|
17
|
+
no automatic release workflow. A future release needs a versioned changelog,
|
|
18
|
+
current validation evidence, security review, and a deliberate maintainer decision.
|
|
19
|
+
Pushing a commit is not a release or proof of compatibility with a new OpenCode
|
|
20
|
+
version. MIT governs reuse; this document governs project contributions.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenCode Lookout contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,240 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenCode Lookout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/sodejm/opencode-lookout/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Inspect agents, skills, plugins, commands, providers, and models in
|
|
7
|
+
OpenCode's terminal interface. The extension appends a compact section to the
|
|
8
|
+
existing sidebar. It does not replace Context or MCP status.
|
|
9
|
+
Use OpenCode's native MCP section or `/mcps` for MCP inventory and controls.
|
|
10
|
+
|
|
11
|
+
Lookout is an independent community extension, not an official OpenCode product.
|
|
12
|
+
It uses the public OpenCode **2.2.5** API contract. Use Node.js **26.4.0+** for a full development install;
|
|
13
|
+
the offline CLI retains **Node.js 22+** support with production dependencies.
|
|
14
|
+
The repository is public under the MIT license. npm publication is a separate,
|
|
15
|
+
manually approved release operation. See the support profiles below before using
|
|
16
|
+
another CLI version.
|
|
17
|
+
|
|
18
|
+
Resource controls require the current V2 policy API (OpenCode 2.2.3+). Older or
|
|
19
|
+
unidentified hosts show a read-only inventory. Release tests require verified
|
|
20
|
+
read-only behavior on 2.2.1–2.2.2 and full controls on newer supported V2 hosts.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
### npm package (v0.1.2)
|
|
25
|
+
|
|
26
|
+
These commands are for use **after v0.1.2 is published**. Install to a dedicated,
|
|
27
|
+
stable prefix, not into another application's dependency tree:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
mkdir -p "$HOME/.local/share/opencode-lookout"
|
|
31
|
+
npm install --prefix "$HOME/.local/share/opencode-lookout" --save-exact --omit=dev --ignore-scripts opencode-lookout@0.1.2
|
|
32
|
+
"$HOME/.local/share/opencode-lookout/node_modules/.bin/lookout" load
|
|
33
|
+
"$HOME/.local/share/opencode-lookout/node_modules/.bin/lookout" status
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use that same executable for `unload` and `restore --yes`, even when OpenCode
|
|
37
|
+
cannot start. The package does not need development dependencies or install
|
|
38
|
+
scripts. OpenCode supplies its SDK and Solid/OpenTUI runtime; these are optional
|
|
39
|
+
peers, not dependencies installed for the offline CLI. The TUI is precompiled
|
|
40
|
+
because OpenTUI does not transform JSX in `node_modules`.
|
|
41
|
+
|
|
42
|
+
Do not use `npx lookout` or an ephemeral npm-exec install for lifecycle commands:
|
|
43
|
+
the owned load entry points to the executable's installed package directory.
|
|
44
|
+
Keep that directory until you have restored restrictions and unloaded Lookout.
|
|
45
|
+
Before switching from a checkout, use the old checkout's `restore --yes` and
|
|
46
|
+
`unload`, then use the new executable's `load`. No personal installation is
|
|
47
|
+
migrated automatically.
|
|
48
|
+
|
|
49
|
+
Direct `opencode plugin add opencode-lookout@...` entries are not owned by the
|
|
50
|
+
offline lifecycle CLI. Do not combine them with the marked path entry. To switch,
|
|
51
|
+
remove the exact unmarked entry through OpenCode first (and check project configs
|
|
52
|
+
for duplicate loads). This release validates the stable-prefix installation,
|
|
53
|
+
not recovery from OpenCode's registry cache. See [recovery](#load-or-unload).
|
|
54
|
+
|
|
55
|
+
### Source checkout
|
|
56
|
+
|
|
57
|
+
Clone outside OpenCode's automatically discovered `plugins/` directory:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
git clone https://github.com/sodejm/opencode-lookout.git
|
|
61
|
+
cd opencode-lookout
|
|
62
|
+
npm ci --ignore-scripts
|
|
63
|
+
make check
|
|
64
|
+
node bin/lookout.mjs load
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Create your global OpenCode configuration through OpenCode first if it does not
|
|
68
|
+
exist. Lookout requires exactly one `opencode.json` or `opencode.jsonc` there;
|
|
69
|
+
it will not create a new config or choose between two files. Keep the checkout
|
|
70
|
+
at its installed path, because the load entry refers to that directory.
|
|
71
|
+
|
|
72
|
+
**Breaking rename:** this revision uses `opencode-lookout` as the plugin/RPC ID,
|
|
73
|
+
`/lookout` as the command, and `lookout:` ownership markers. There are no legacy
|
|
74
|
+
aliases. Before replacing an older installation, use its own recovery CLI to
|
|
75
|
+
restore its restrictions and unload it. Do not run both copies. Reinstall only
|
|
76
|
+
after the remote release is complete; pins and collapsed sections start fresh.
|
|
77
|
+
The new CLI does not claim or remove old ownership blocks.
|
|
78
|
+
|
|
79
|
+
## Load or unload
|
|
80
|
+
|
|
81
|
+
Run these commands **from this package's directory**, outside OpenCode:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
node bin/lookout.mjs load
|
|
85
|
+
node bin/lookout.mjs status
|
|
86
|
+
node bin/lookout.mjs unload
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The commands work without the extension, server, or TUI running. They change one
|
|
90
|
+
marked entry in your global `opencode.jsonc` or `opencode.json`. They refuse to
|
|
91
|
+
choose between those files if both exist. They respect `XDG_CONFIG_HOME`.
|
|
92
|
+
|
|
93
|
+
OpenCode watches configuration changes. Use `/reload` if the UI has not updated.
|
|
94
|
+
`status` reports the configuration on disk, **not** whether the extension loaded.
|
|
95
|
+
Check `opencode plugin list` and open `/lookout` to verify both parts.
|
|
96
|
+
|
|
97
|
+
Unloading keeps resource restrictions in place. Removing a UI must not silently
|
|
98
|
+
restore tool access. To remove restrictions created by this extension:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
node bin/lookout.mjs restore --yes
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This affects all projects using the server. It removes only marked Lookout
|
|
105
|
+
entries. Existing permissions, policies, credentials, and plugin entries remain.
|
|
106
|
+
|
|
107
|
+
## Browse the catalog
|
|
108
|
+
|
|
109
|
+
Run `/lookout`, or choose **Open Lookout** in the command palette.
|
|
110
|
+
Use `/lookout skill` to start with one category. Category names are `agent`,
|
|
111
|
+
`skill`, `plugin`, `command`, `provider`, and `model`.
|
|
112
|
+
|
|
113
|
+
The catalog uses OpenCode's native searchable dialogs:
|
|
114
|
+
|
|
115
|
+
| Action | Control |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| Search names, IDs, descriptions, and model details | Type in the search field |
|
|
118
|
+
| Change category | Ctrl+F |
|
|
119
|
+
| Filter by state | Ctrl+S |
|
|
120
|
+
| Refresh the inventory | Ctrl+R |
|
|
121
|
+
| Inspect an item or change it | Enter |
|
|
122
|
+
| Return or close | Escape |
|
|
123
|
+
|
|
124
|
+
Selecting an item opens its action menu. **Show details** opens a read-only
|
|
125
|
+
inspector; Escape closes it back to the prompt. Run `/lookout` again to
|
|
126
|
+
browse another item.
|
|
127
|
+
|
|
128
|
+
Sidebar headings expand or collapse on click. Each expanded category shows up to
|
|
129
|
+
three entries. Select an item and choose **Pin to sidebar** to choose which entries
|
|
130
|
+
appear. Pins and collapsed sections are terminal presentation preferences; they
|
|
131
|
+
do not enable or disable resources. Keyboard-only users can access every resource
|
|
132
|
+
and action through `/lookout`, even when the terminal hides the sidebar.
|
|
133
|
+
|
|
134
|
+
Status dots use the native sidebar's semantic theme colors: green for available
|
|
135
|
+
or active entries, yellow for pending, stale, unknown, or still-listed restrictions,
|
|
136
|
+
grey for disabled or inactive entries, and red for failures. The selected agent
|
|
137
|
+
also has an arrow. Green reflects catalog availability, not verified provider
|
|
138
|
+
authentication, quota, or successful execution. Open an item's details for its
|
|
139
|
+
text status.
|
|
140
|
+
|
|
141
|
+
Plugin rows include a description from their own files. For local plugin
|
|
142
|
+
directories, Lookout reads the `description` string in that directory's
|
|
143
|
+
`package.json`. For single-file plugins, it reads a leading JSDoc tag:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
/** @description Explain what this plugin does. */
|
|
147
|
+
export default { id: "example.plugin", setup() {} };
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If metadata is missing, unreadable, or unsupported, the row says
|
|
151
|
+
`missing description`. Lookout does not execute plugin code, search parent
|
|
152
|
+
packages, fetch registry metadata, or keep a hardcoded description list. Packaged
|
|
153
|
+
or built-in plugins whose catalog entry has no local source path use the fallback.
|
|
154
|
+
Descriptions are truncated in the sidebar; open **Show details** to read more.
|
|
155
|
+
|
|
156
|
+
## What the controls mean
|
|
157
|
+
|
|
158
|
+
Resource changes require a preview and **Apply Globally** confirmation. They affect
|
|
159
|
+
every project using that server, not just the current conversation. Wait for other
|
|
160
|
+
work to finish before changing resources it may use.
|
|
161
|
+
|
|
162
|
+
| Resource | Supported control |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| Skills from configured local sources | Add an exact `skill.use` denial |
|
|
165
|
+
| Local plugins inside your OpenCode configuration directory | Add an exact `-plugin-id` entry |
|
|
166
|
+
| Agents | Inspect; use `/agents` to select |
|
|
167
|
+
| Commands, providers, models | Inspect; use native OpenCode or Console controls |
|
|
168
|
+
| Built-in, managed, unknown-source plugins | Read-only |
|
|
169
|
+
| Lookout itself | Use the external `unload` command |
|
|
170
|
+
|
|
171
|
+
**Enable** removes this extension's denial. It never adds an `allow`, removes an
|
|
172
|
+
external restriction, or overrides managed policy. A missing resource stays in
|
|
173
|
+
the catalog as a disabled row when Lookout owns its restriction, so you can
|
|
174
|
+
restore it after a reload or restart.
|
|
175
|
+
|
|
176
|
+
Lookout MCP restrictions are preserved, not silently removed when the inventory
|
|
177
|
+
is hidden. Use the external `restore --yes` command to remove all Lookout
|
|
178
|
+
restrictions, including MCP entries. The MCP RPC contract remains available for
|
|
179
|
+
recovery; it is not exposed in the browser.
|
|
180
|
+
|
|
181
|
+
**Restricted; still listed** means the config entry exists but the live catalog
|
|
182
|
+
still includes the resource. Do not interpret that state as successful blocking.
|
|
183
|
+
Higher-priority policy, reload timing, or plugin ordering may explain the result.
|
|
184
|
+
Refresh and use the native controls to verify access. Disabling a skill does not
|
|
185
|
+
remove instructions already loaded into an existing conversation.
|
|
186
|
+
|
|
187
|
+
## Troubleshoot
|
|
188
|
+
|
|
189
|
+
1. Run `node bin/lookout.mjs status` to inspect the load entry and owned
|
|
190
|
+
restriction count. No credentials are needed.
|
|
191
|
+
2. Run `node bin/lookout.mjs unload` to remove both extension parts from
|
|
192
|
+
configuration. The package lives outside automatic plugin discovery.
|
|
193
|
+
3. Use `/reload`. If only the terminal is unusable, open a terminal without its UI:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
OPENCODE_CLI_CONFIG_CONTENT='{"plugins":["*","-opencode-lookout"]}' opencode
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
This suppresses only the TUI part for that process. Use `unload` for the server
|
|
200
|
+
part. It does not bypass any policy or remove resource restrictions.
|
|
201
|
+
4. Fix the failure, run `make check`, and use `load` to re-enable the extension.
|
|
202
|
+
|
|
203
|
+
If an apply request loses its response during configuration reload, refresh before
|
|
204
|
+
retrying: the write might already have succeeded. If a `.lookout-lock` file remains
|
|
205
|
+
after a crash, first ensure no Lookout command is running. Inspect the lock
|
|
206
|
+
beside your global config, then remove that lock manually and retry. Never delete
|
|
207
|
+
the global configuration to clear a lock.
|
|
208
|
+
|
|
209
|
+
Do not edit text inside `lookout:begin` / `lookout:end` ownership markers. The controller
|
|
210
|
+
rejects modified blocks rather than risk deleting your edits. Unload still works
|
|
211
|
+
without calling the server. Preserve the file and repair the marked block manually
|
|
212
|
+
if normal restore cannot parse it.
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
npm ci --ignore-scripts
|
|
218
|
+
make check
|
|
219
|
+
npm run test:integration
|
|
220
|
+
npm run test:tui
|
|
221
|
+
LOOKOUT_TUI=1 LOOKOUT_NARROW=1 LOOKOUT_THEME=tokyonight npm run test:integration
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The integration checks require the `opencode` executable. Terminal checks also
|
|
225
|
+
require `/usr/bin/expect`. They use a temporary HOME and deny provider and tool
|
|
226
|
+
use. Set `LOOKOUT_KEEP_FIXTURE=1` to retain a failed fixture for local inspection; do not
|
|
227
|
+
share its credential files or raw terminal transcript.
|
|
228
|
+
|
|
229
|
+
See [Architecture](docs/ARCHITECTURE.md) for module boundaries and configuration
|
|
230
|
+
ownership, [Threat model](docs/THREAT_MODEL.md) for safety limits, and
|
|
231
|
+
[Validation](docs/VALIDATION.md) for evidence and remaining manual checks.
|
|
232
|
+
|
|
233
|
+
## Project information
|
|
234
|
+
|
|
235
|
+
- [Contributing](CONTRIBUTING.md): setup, tests, and review expectations.
|
|
236
|
+
- [Security](SECURITY.md): supported versions and private vulnerability reports.
|
|
237
|
+
- [Support](SUPPORT.md): troubleshooting and useful bug reports.
|
|
238
|
+
- [Code of conduct](CODE_OF_CONDUCT.md) and [governance](GOVERNANCE.md).
|
|
239
|
+
- [Changelog](CHANGELOG.md) and [MIT license](LICENSE).
|
|
240
|
+
- [Repository metadata and release policy](docs/RELEASING.md).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Lookout runs with the filesystem authority of your OpenCode process. Its controls
|
|
4
|
+
edit global configuration and can affect other sessions. It is not a sandbox.
|
|
5
|
+
Read the [threat model](docs/THREAT_MODEL.md) before enabling resource controls.
|
|
6
|
+
|
|
7
|
+
## Supported versions
|
|
8
|
+
|
|
9
|
+
Security fixes target the current `main` branch. The initial package version is
|
|
10
|
+
0.1.0, targeting OpenCode 2.2.5. The release matrix requires read-only behavior on
|
|
11
|
+
2.2.1–2.2.2 and full controls on supported V2 versions from 2.2.3 onward. Consult
|
|
12
|
+
the exact release's test evidence; the API version floor is not a blanket claim
|
|
13
|
+
that every future CLI is compatible. There are no maintained older release
|
|
14
|
+
branches or guaranteed response times.
|
|
15
|
+
|
|
16
|
+
## Report a vulnerability privately
|
|
17
|
+
|
|
18
|
+
Use [GitHub's private vulnerability report form](https://github.com/sodejm/opencode-lookout/security/advisories/new).
|
|
19
|
+
Include affected revisions, impact, and a minimal reproduction with synthetic
|
|
20
|
+
data. Do not open a public issue containing exploit details, credentials, global
|
|
21
|
+
configuration, or retained test transcripts. If the private form is unavailable,
|
|
22
|
+
open an issue asking for a private reporting channel without technical details.
|
|
23
|
+
|
|
24
|
+
If a credential was exposed, revoke or rotate it through its provider. Removing
|
|
25
|
+
it from an issue or commit is not enough. Do not send the credential to maintainers.
|
|
26
|
+
|
|
27
|
+
The maintainer will assess the report, coordinate a fix and disclosure, and credit
|
|
28
|
+
reporters who want attribution. Automated checks are not security certification.
|
|
29
|
+
|
|
30
|
+
## Safe recovery
|
|
31
|
+
|
|
32
|
+
From the installed checkout, run `node bin/lookout.mjs unload`. This works
|
|
33
|
+
without OpenCode and preserves resource restrictions. Use `restore --yes` only
|
|
34
|
+
when you intend to remove all restrictions owned by this extension. Never delete
|
|
35
|
+
the global configuration to recover. See the [recovery guide](README.md#troubleshoot).
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Support
|
|
2
|
+
|
|
3
|
+
For usage questions, open a [GitHub issue](https://github.com/sodejm/opencode-lookout/issues).
|
|
4
|
+
For vulnerabilities, follow [Security](SECURITY.md) instead. This is a volunteer
|
|
5
|
+
project; support has no service-level or response-time guarantee.
|
|
6
|
+
|
|
7
|
+
Before reporting a problem:
|
|
8
|
+
|
|
9
|
+
1. Read the [troubleshooting steps](README.md#troubleshoot).
|
|
10
|
+
2. Record your OpenCode and Node.js versions, operating system, terminal, theme,
|
|
11
|
+
and approximate terminal dimensions.
|
|
12
|
+
3. Run `node bin/lookout.mjs status` from the checkout. It reports disk
|
|
13
|
+
configuration, not proof that the server or UI loaded successfully.
|
|
14
|
+
4. Describe expected and actual behavior, steps to reproduce, and whether external
|
|
15
|
+
unload restores normal behavior. Include the Lookout commit ID if available.
|
|
16
|
+
|
|
17
|
+
Share only sanitized excerpts. Remove tokens, personal paths, prompts, session
|
|
18
|
+
identifiers, MCP commands, and private server addresses. Do not attach your full
|
|
19
|
+
global config, logs, or retained integration fixtures. Reproduce with synthetic
|
|
20
|
+
data when possible.
|
package/bin/lookout.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Offline lifecycle controls. Never require a running server, TUI, or credentials. */
|
|
3
|
+
import { access, realpath } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { ID, lifecycle, owned, change, parseConfig } from "../src/config.mjs";
|
|
8
|
+
import { atomicUpdate, readConfig } from "../src/controller.mjs";
|
|
9
|
+
|
|
10
|
+
/** Resolve only the user's global config; fail rather than choose between two files. */
|
|
11
|
+
export async function configFile() {
|
|
12
|
+
const directory = join(
|
|
13
|
+
process.env.XDG_CONFIG_HOME || join(homedir(), ".config"),
|
|
14
|
+
"opencode",
|
|
15
|
+
);
|
|
16
|
+
const files = [];
|
|
17
|
+
for (const name of ["opencode.jsonc", "opencode.json"]) {
|
|
18
|
+
const file = join(directory, name);
|
|
19
|
+
if (
|
|
20
|
+
await access(file).then(
|
|
21
|
+
() => true,
|
|
22
|
+
() => false,
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
files.push(file);
|
|
26
|
+
}
|
|
27
|
+
if (files.length !== 1)
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Expected exactly one global opencode.json or opencode.jsonc. No files changed.",
|
|
30
|
+
);
|
|
31
|
+
return files[0];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* load/unload changes only the marked package entry. restore removes all owned
|
|
36
|
+
* resource restrictions and requires --yes because it can restore tool access.
|
|
37
|
+
* Status describes disk configuration, not server or TUI readiness.
|
|
38
|
+
* @param {string[]} args CLI arguments, excluding the executable and script path.
|
|
39
|
+
* @returns {Promise<void>} Resolves after the requested operation and status output.
|
|
40
|
+
*/
|
|
41
|
+
export async function main(args = process.argv.slice(2)) {
|
|
42
|
+
const [command = "status", confirmation] = args;
|
|
43
|
+
if (
|
|
44
|
+
!["load", "unload", "status", "restore"].includes(command) ||
|
|
45
|
+
args.length > 2 ||
|
|
46
|
+
(confirmation !== undefined &&
|
|
47
|
+
(command !== "restore" || confirmation !== "--yes"))
|
|
48
|
+
)
|
|
49
|
+
throw new Error(
|
|
50
|
+
"Usage: node bin/lookout.mjs [load|unload|status|restore --yes]",
|
|
51
|
+
);
|
|
52
|
+
if (command === "restore" && confirmation !== "--yes")
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Restore removes every Lookout restriction globally. Repeat with restore --yes to confirm.",
|
|
55
|
+
);
|
|
56
|
+
const file = await configFile();
|
|
57
|
+
const packagePath = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
58
|
+
if (command === "load" || command === "unload") {
|
|
59
|
+
await atomicUpdate(file, null, (text) =>
|
|
60
|
+
lifecycle(text, packagePath, command === "load"),
|
|
61
|
+
);
|
|
62
|
+
console.log(
|
|
63
|
+
`Lookout ${command === "load" ? "load" : "unload"} configured. OpenCode watches this file; use /reload if needed.`,
|
|
64
|
+
);
|
|
65
|
+
if (command === "unload")
|
|
66
|
+
console.log(
|
|
67
|
+
"Resource restrictions were preserved. Use restore --yes separately to remove them.",
|
|
68
|
+
);
|
|
69
|
+
} else if (command === "restore") {
|
|
70
|
+
await atomicUpdate(file, null, (text) =>
|
|
71
|
+
owned(text).reduce((next, r) => change(next, r.kind, r.id, true), text),
|
|
72
|
+
);
|
|
73
|
+
console.log(
|
|
74
|
+
"Removed Lookout restrictions only. Other policies and disabled settings remain unchanged.",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const { text } = await readConfig(file);
|
|
78
|
+
const value = parseConfig(text).plugins;
|
|
79
|
+
const plugins = Array.isArray(value) ? value : [];
|
|
80
|
+
console.log(
|
|
81
|
+
`Package entry: ${plugins.includes(packagePath) ? "present" : plugins.includes("-" + ID) ? "disabled" : "not configured"}`,
|
|
82
|
+
);
|
|
83
|
+
console.log(`Owned restrictions: ${owned(text).length}`);
|
|
84
|
+
}
|
|
85
|
+
if (
|
|
86
|
+
process.argv[1] &&
|
|
87
|
+
// npm invokes the executable through node_modules/.bin (a symlink on Unix).
|
|
88
|
+
(await realpath(process.argv[1]).catch(() => "")) ===
|
|
89
|
+
fileURLToPath(import.meta.url)
|
|
90
|
+
) {
|
|
91
|
+
main().catch((error) => {
|
|
92
|
+
console.error(
|
|
93
|
+
error?.code
|
|
94
|
+
? "Cannot access configuration safely. Check permissions; no credentials are needed."
|
|
95
|
+
: error.message,
|
|
96
|
+
);
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
});
|
|
99
|
+
}
|