@p10i/create-nestbox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +34 -0
- package/.env.example +35 -0
- package/.gitattributes +10 -0
- package/INSTALL.md +258 -0
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/bin/create-nestbox.js +184 -0
- package/docker/nginx/Dockerfile +31 -0
- package/docker/opencode/Dockerfile +36 -0
- package/docker/opencode/entrypoint.sh +9 -0
- package/docker/php-fpm/Dockerfile +43 -0
- package/docker/rollup/Dockerfile +16 -0
- package/docker-compose.yaml +139 -0
- package/home/code/.user.ini +1 -0
- package/home/code/404.php +104 -0
- package/home/code/__data/.gitignore +2 -0
- package/home/code/__includes/footer.php +3 -0
- package/home/code/__includes/functions.php +325 -0
- package/home/code/__includes/header.php +20 -0
- package/home/code/__templates/super-document.php +47 -0
- package/home/code/scripts-bundle.css +13295 -0
- package/home/code/scripts-bundle.js +6277 -0
- package/home/code/scripts.js +3 -0
- package/home/code/styles.css +36 -0
- package/home/configs/nginx/nginx.conf +199 -0
- package/home/configs/opencode/instance.example.md +16 -0
- package/home/configs/opencode/instructions.md +308 -0
- package/home/configs/opencode/opencode.json +15 -0
- package/home/configs/php/cli.php +33 -0
- package/home/configs/php/composer.json +10 -0
- package/home/configs/php/composer.lock +805 -0
- package/home/configs/php/index.php +105 -0
- package/home/configs/php/init.php +3 -0
- package/home/configs/php/php.ini +5 -0
- package/home/configs/php/preload.php +5 -0
- package/home/configs/rollup/package-lock.json +2089 -0
- package/home/configs/rollup/package.json +16 -0
- package/home/configs/rollup/rollup.config.js +28 -0
- package/package.json +42 -0
- package/scripts/check-package.js +31 -0
- package/template.gitignore +20 -0
- package/tests/nestbox.ps1 +322 -0
- package/tests/nestbox.sh +438 -0
package/.dockerignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Build context hygiene — keep image builds small and reproducible.
|
|
2
|
+
|
|
3
|
+
# VCS / IDE
|
|
4
|
+
.git
|
|
5
|
+
**/.git
|
|
6
|
+
.gitignore
|
|
7
|
+
.vscode
|
|
8
|
+
.idea
|
|
9
|
+
|
|
10
|
+
# Local environment
|
|
11
|
+
.env
|
|
12
|
+
.env.*
|
|
13
|
+
!.env.example
|
|
14
|
+
|
|
15
|
+
# npm / Composer caches that may live in the working tree
|
|
16
|
+
node_modules
|
|
17
|
+
vendor
|
|
18
|
+
|
|
19
|
+
# Page state is local by default and must not leak into image layers. A page
|
|
20
|
+
# that deliberately ships durable data must explicitly revise this policy.
|
|
21
|
+
home/code/**/__data/**
|
|
22
|
+
|
|
23
|
+
# OS junk
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Bundles are produced inside the build, not pulled from the host.
|
|
28
|
+
home/code/scripts-bundle.js
|
|
29
|
+
home/code/scripts-bundle.css
|
|
30
|
+
|
|
31
|
+
# Docs and metadata not needed at build time
|
|
32
|
+
README.md
|
|
33
|
+
LICENSE
|
|
34
|
+
tests
|
package/.env.example
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copy this file to .env next to docker-compose.yaml and adjust as needed.
|
|
2
|
+
|
|
3
|
+
# Docker Compose
|
|
4
|
+
# Set a unique slug for this project before starting Nestbox.
|
|
5
|
+
COMPOSE_PROJECT_NAME=nestbox-change-me
|
|
6
|
+
|
|
7
|
+
# Workspace exposed to PHP and OpenCode. Use . when Nestbox fills the selected
|
|
8
|
+
# directory, or .. when this installation is <workspace>/.nestbox.
|
|
9
|
+
WORKSPACE_PATH=.
|
|
10
|
+
|
|
11
|
+
# Use 127.0.0.1 for local access only or 0.0.0.0 for all interfaces.
|
|
12
|
+
BIND_ADDRESS=127.0.0.1
|
|
13
|
+
|
|
14
|
+
# Published port shared by Nestbox and OpenCode. The installer confirms this
|
|
15
|
+
# default is available and asks before selecting another port.
|
|
16
|
+
WEB_PORT=4180
|
|
17
|
+
|
|
18
|
+
# Optional public path when a reverse proxy strips this prefix before forwarding.
|
|
19
|
+
NESTBOX_URL_PREFIX=
|
|
20
|
+
|
|
21
|
+
# Optional absolute browser origin when the agent.* host cannot be derived from
|
|
22
|
+
# the incoming Nestbox host, for example https://agent.project.example.
|
|
23
|
+
NESTBOX_OPENCODE_PUBLIC_URL=
|
|
24
|
+
|
|
25
|
+
# Runtime ownership
|
|
26
|
+
APP_UID=1000
|
|
27
|
+
APP_GID=1000
|
|
28
|
+
|
|
29
|
+
# Secrets
|
|
30
|
+
# OpenCode receives every variable in this file. Keep unrelated secrets out.
|
|
31
|
+
# Leave the server password empty to disable HTTP basic authentication.
|
|
32
|
+
OPENCODE_SERVER_USERNAME=opencode
|
|
33
|
+
OPENCODE_SERVER_PASSWORD=
|
|
34
|
+
ANTHROPIC_API_KEY=
|
|
35
|
+
OPENAI_API_KEY=
|
package/.gitattributes
ADDED
package/INSTALL.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Agent Installation Guide
|
|
2
|
+
|
|
3
|
+
Follow this protocol when installing Nestbox into a selected directory or at `<workspace>/.nestbox`.
|
|
4
|
+
|
|
5
|
+
## Npm Creator
|
|
6
|
+
|
|
7
|
+
For ordinary new projects, prefer the npm creator:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm create @p10i/nestbox@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The published npm package is `@p10i/create-nestbox`; npm maps `npm create @p10i/nestbox` to that package. The creator scaffolds one of the two default layouts, copies `.env.example` to `.env`, copies `home/configs/opencode/instance.example.md` to `instance.md`, records npm package provenance in the instance policy, and initializes fresh installation Git history when Git is available.
|
|
14
|
+
|
|
15
|
+
The creator does not start Docker or collect secrets. After it finishes, edit `.env`, then run from the installation directory:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
docker compose config --quiet
|
|
19
|
+
docker compose up -d --build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then run the host test suite documented in section 7.
|
|
23
|
+
|
|
24
|
+
## 1. Choose Language
|
|
25
|
+
|
|
26
|
+
Before technical questions, ask which language to use for installation communication. Then distinguish three independent choices:
|
|
27
|
+
|
|
28
|
+
- agent communication only in that language;
|
|
29
|
+
- a translated Nestbox interface;
|
|
30
|
+
- an extensible i18n setup for multiple interface languages.
|
|
31
|
+
|
|
32
|
+
Record the communication and interface choices in `home/configs/opencode/instance.md`. Nestbox source is English by default. Translation and i18n are feature work for the internal OpenCode agent after basic startup, not a reason for the external installer to maintain a second implementation path.
|
|
33
|
+
|
|
34
|
+
## 2. Choose One Default Layout
|
|
35
|
+
|
|
36
|
+
Resolve and report the absolute selected directory. Offer exactly two defaults:
|
|
37
|
+
|
|
38
|
+
1. **Current directory:** install Nestbox directly into the selected directory, then initialize fresh installation history at `<selected>/.git`.
|
|
39
|
+
2. **`.nestbox`:** treat the selected directory as the workspace, install Nestbox at `<workspace>/.nestbox`, then initialize fresh installation history at `<workspace>/.nestbox/.git`.
|
|
40
|
+
|
|
41
|
+
Both defaults keep pages, Compose, Dockerfiles, configuration, dependencies, and documentation in the same fresh installation repository. Pages remain under the installation's `home/code`. Upstream Nestbox commit history is not imported into the user's installation history.
|
|
42
|
+
|
|
43
|
+
Do not present vendoring, a parent-owned engine, a submodule, an external page tree, or another shared-repository arrangement as a default. These are optional adaptations discussed only when the user requests one.
|
|
44
|
+
|
|
45
|
+
Never overwrite an existing installation. Inspect an existing target and ask whether to update, repair, reuse, or stop.
|
|
46
|
+
|
|
47
|
+
## 3. Check Requirements And Git
|
|
48
|
+
|
|
49
|
+
Run:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
git --version
|
|
53
|
+
docker version
|
|
54
|
+
docker compose version
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Nestbox requires Git, a running Docker Engine, and Docker Compose v2. If something is missing, explain the platform-specific requirement and pause. Do not install system software without approval.
|
|
58
|
+
|
|
59
|
+
Inspect Git status for the selected directory and installation target before changing files. Preserve unrelated changes. The fresh Nestbox repository remains independent in both default layouts, including when `.nestbox` is nested inside another Git workspace.
|
|
60
|
+
|
|
61
|
+
If the `.nestbox` target is inside another Git repository, ask whether to exclude `.nestbox/` through that repository's local `.git/info/exclude` or tracked `.gitignore`. Do not modify the parent repository without approval.
|
|
62
|
+
|
|
63
|
+
## 4. Create The Installation
|
|
64
|
+
|
|
65
|
+
For the current-directory layout, shallow-clone the source into the selected empty directory:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
git clone --depth 1 https://github.com/piontkovsk11andre1/nestbox .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For the `.nestbox` layout, run from the confirmed workspace:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
git clone --depth 1 https://github.com/piontkovsk11andre1/nestbox .nestbox
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Immediately after cloning:
|
|
78
|
+
|
|
79
|
+
1. Resolve and record the source revision with `git rev-parse HEAD` from the clone.
|
|
80
|
+
2. Remove only the `.git` directory created by this new clone. Never remove Git metadata from a pre-existing path.
|
|
81
|
+
3. Run `git init` in the installation directory.
|
|
82
|
+
4. Continue configuration and verification before creating the installation baseline commit.
|
|
83
|
+
|
|
84
|
+
The resulting `.git` contains only this installation's history. Keeping upstream history or adding an upstream remote is an optional adaptation, not a default.
|
|
85
|
+
|
|
86
|
+
## 5. Configure The Instance
|
|
87
|
+
|
|
88
|
+
Copy `.env.example` to `.env` and `home/configs/opencode/instance.example.md` to `home/configs/opencode/instance.md` in the installation directory. Both defaults use the installation's `./home/code` page tree. Configure the workspace mount as follows:
|
|
89
|
+
|
|
90
|
+
| Layout | `WORKSPACE_PATH` |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| Current directory | `.` |
|
|
93
|
+
| `<workspace>/.nestbox` | `..` |
|
|
94
|
+
|
|
95
|
+
Relative bind paths resolve from the installation directory. The page tree is mounted as `/home/code` in runtime services and as the writable `/nestbox/home/code` agent path. `WORKSPACE_PATH` is mounted as `/workspace` in PHP and OpenCode. `NESTBOX_PAGE_PATH` remains an optional advanced override for a user-requested external page tree.
|
|
96
|
+
|
|
97
|
+
Update the new `home/configs/opencode/instance.md` with:
|
|
98
|
+
|
|
99
|
+
- the communication language;
|
|
100
|
+
- interface language or requested i18n direction;
|
|
101
|
+
- installation and workspace paths;
|
|
102
|
+
- the Nestbox Git root;
|
|
103
|
+
- the recorded upstream source revision;
|
|
104
|
+
- whether verified Nestbox changes are committed automatically, require per-commit confirmation, or remain uncommitted.
|
|
105
|
+
|
|
106
|
+
This is agent guidance and belongs in `instance.md`, not `.env`.
|
|
107
|
+
|
|
108
|
+
Then ask about:
|
|
109
|
+
|
|
110
|
+
- a unique lowercase `COMPOSE_PROJECT_NAME`;
|
|
111
|
+
- whether to use default port `4180`; verify availability and ask before choosing another port;
|
|
112
|
+
- loopback-only or network access;
|
|
113
|
+
- OpenCode username and password;
|
|
114
|
+
- AI providers;
|
|
115
|
+
- Linux UID and GID when bind-mount ownership matters;
|
|
116
|
+
- additional directories required by PHP and OpenCode;
|
|
117
|
+
- an optional reverse-proxy URL prefix;
|
|
118
|
+
- rolling or pinned container images;
|
|
119
|
+
- `NESTBOX_OPENCODE_PUBLIC_URL` when `agent.<nestbox-host>` cannot be derived.
|
|
120
|
+
|
|
121
|
+
Open `.env` in a local editor for runtime settings and secrets. Never ask the user to paste provider keys into chat or print the completed file.
|
|
122
|
+
|
|
123
|
+
Before startup, query existing Docker containers with the proposed Compose project label. Reject the name if an existing container belongs to another Compose working directory or uses a service not defined by this installation. Do not remove foreign containers or volumes automatically.
|
|
124
|
+
|
|
125
|
+
For additional directories, mount the same host path at the same container path in PHP and OpenCode. Extend OpenCode's `external_directory` permission when unattended access is intended. Prefer read-only mounts unless writing is required.
|
|
126
|
+
|
|
127
|
+
Keep PHP-FPM and OpenCode's PHP CLI on the same PHP version. For a path-prefixed reverse proxy, the outer proxy removes the prefix before forwarding and configures `NESTBOX_URL_PREFIX`. Set `NESTBOX_OPENCODE_PUBLIC_URL` explicitly whenever the public OpenCode scheme or host differs from direct Nestbox access.
|
|
128
|
+
|
|
129
|
+
## 6. Keep The Public Page Tree Empty
|
|
130
|
+
|
|
131
|
+
The installation already contains Nestbox support files in `home/code`: `404.php`, protected includes and templates, browser sources, and generated asset baselines. Do not create `index.php` or another public project page.
|
|
132
|
+
|
|
133
|
+
Opening `/` must retain HTTP 404 and render the form that starts an OpenCode session for the first screen. Any route segment beginning with `__` remains private over HTTP and through the CLI router.
|
|
134
|
+
|
|
135
|
+
## 7. Start And Verify
|
|
136
|
+
|
|
137
|
+
Run from the installation directory:
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
docker compose config --quiet
|
|
141
|
+
docker compose up -d --build
|
|
142
|
+
docker compose ps
|
|
143
|
+
docker compose port nginx 80
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Verify:
|
|
147
|
+
|
|
148
|
+
- `/` retains HTTP 404 and renders the OpenCode implementation form;
|
|
149
|
+
- `/__templates/super-document` returns 404;
|
|
150
|
+
- `php /sources/php/cli.php __templates/super-document` is rejected;
|
|
151
|
+
- `/Agent` redirects to OpenCode on the same published port;
|
|
152
|
+
- the actual OpenCode URL works in the user's client;
|
|
153
|
+
- a provider and model are available for agent work, without printing credentials or sending a billable prompt unless the user approves it;
|
|
154
|
+
- `/_nestbox/health` returns HTTP 200;
|
|
155
|
+
- a transient PHP event reaches an EventSource subscriber through Nchan;
|
|
156
|
+
- every `__data` directory remains inaccessible over HTTP;
|
|
157
|
+
- no service repeatedly restarts.
|
|
158
|
+
|
|
159
|
+
Compare the resolved Compose bind sources with the running containers after startup. Recreate a service when its actual mounts do not match the current Compose model; do not claim a path change is active merely because `docker compose config` is valid.
|
|
160
|
+
|
|
161
|
+
Run the host test suite:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
bash tests/nestbox.sh
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```powershell
|
|
168
|
+
.\tests\nestbox.ps1
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The scripts support both the fresh 404-first tree and later installations with `index.php`. `--start` for Bash and `-Start` for PowerShell may build and start application services first. Lifecycle mode never runs `down`, removes volumes, or starts an absent Rollup watcher that could rewrite bundles.
|
|
172
|
+
|
|
173
|
+
Some clients resolve `.localhost` internally even when an operating-system DNS query does not. Test the actual OpenCode URL. If it fails to resolve, ask before adding this hosts-file entry:
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
127.0.0.1 agent.localhost
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
For another device, configure explicit names for both Nestbox and OpenCode instead of relying on `agent.localhost`.
|
|
180
|
+
|
|
181
|
+
## 8. Integrate The Host Agent
|
|
182
|
+
|
|
183
|
+
Before handoff, register this Nestbox location with the external installation agent so future host-maintenance requests resolve the correct installation through the agent's own integration mechanism.
|
|
184
|
+
|
|
185
|
+
Use the best integration mechanism supported by that agent, in this order:
|
|
186
|
+
|
|
187
|
+
1. a project-local or user-local agent skill;
|
|
188
|
+
2. an agent-native project command, workspace integration, plugin, or configuration entry;
|
|
189
|
+
3. a small start/status/maintenance script that invokes the agent with the required context.
|
|
190
|
+
|
|
191
|
+
The integration must record or supply:
|
|
192
|
+
|
|
193
|
+
- the absolute installation, workspace, and Git-root paths;
|
|
194
|
+
- the Nestbox and OpenCode URLs;
|
|
195
|
+
- the selected communication language;
|
|
196
|
+
- the path to this `INSTALL.md` and `home/configs/opencode/instructions.md`;
|
|
197
|
+
- that page, feature, translation, package, service, and integration work belongs to the internal OpenCode agent;
|
|
198
|
+
- that the external agent handles installation, repair, operating-system integration, and host-side Compose validation and activation;
|
|
199
|
+
- the recorded commit behavior and repository boundary;
|
|
200
|
+
- the normal host test command.
|
|
201
|
+
|
|
202
|
+
Do not put `.env` contents, credentials, provider keys, or passwords into the integration. Prefer project-local scope. Ask before modifying global agent configuration, user-wide skills, shell profiles, or operating-system paths.
|
|
203
|
+
|
|
204
|
+
Verify that the resulting skill, command, or script can identify this exact installation and state its maintenance boundary. Report what was created and where. If the current agent cannot persist an integration, provide the user with the exact reusable prompt or command needed to restore this context later.
|
|
205
|
+
|
|
206
|
+
## 9. Handoff And Extension Request
|
|
207
|
+
|
|
208
|
+
Report:
|
|
209
|
+
|
|
210
|
+
- Nestbox and OpenCode URLs without credentials;
|
|
211
|
+
- installation, workspace, and Git-root paths;
|
|
212
|
+
- selected communication and interface language;
|
|
213
|
+
- commit behavior;
|
|
214
|
+
- remaining uncommitted changes.
|
|
215
|
+
|
|
216
|
+
Ask the user to open OpenCode. Then ask what they want to change or extend in Nestbox. Typical requests include:
|
|
217
|
+
|
|
218
|
+
- creating the first screen or another super-document;
|
|
219
|
+
- translating the interface or adding i18n;
|
|
220
|
+
- integrating a Python program, database, worker, native tool, or host service;
|
|
221
|
+
- installing browser, PHP, system, or agent-toolchain packages;
|
|
222
|
+
- adding mounts, networks, proxy routes, or persistent volumes.
|
|
223
|
+
|
|
224
|
+
Send this work to the internal OpenCode agent. A useful prompt is:
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
Extend this Nestbox installation for the following requirement: <request>. Inspect /workspace and /nestbox, follow the internal Nestbox manual, implement and verify all file changes you can, and report the exact host-side activation command for any container or infrastructure change.
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The internal agent edits pages and infrastructure definitions. It has no Docker socket and must not claim host activation. The external maintainer resumes only to inspect the proposed infrastructure files, run `docker compose config`, apply the narrowest build or recreate operation, and verify health.
|
|
231
|
+
|
|
232
|
+
## 10. Git And Generated State
|
|
233
|
+
|
|
234
|
+
The fresh Git root is the installation's `.git` in current-directory mode or `.nestbox/.git` in `.nestbox` mode. It owns reasonable Nestbox source changes, including:
|
|
235
|
+
|
|
236
|
+
- super-documents and selected durable `__data` files explicitly opted into Git history;
|
|
237
|
+
- reusable helpers and browser source;
|
|
238
|
+
- generated browser bundle baselines, which keep the initial interface available before the watcher starts;
|
|
239
|
+
- Compose files and Dockerfiles;
|
|
240
|
+
- Nginx, PHP, Rollup, and OpenCode configuration;
|
|
241
|
+
- portable integration definitions and dependency lockfiles;
|
|
242
|
+
- documentation and translation resources.
|
|
243
|
+
|
|
244
|
+
Follow the commit behavior recorded in `instance.md`:
|
|
245
|
+
|
|
246
|
+
- automatic: create a focused commit after verification;
|
|
247
|
+
- confirmation: show the exact paths and ask before each commit;
|
|
248
|
+
- no-commit: leave changes uncommitted.
|
|
249
|
+
|
|
250
|
+
After basic verification, create or offer one focused baseline commit containing the installed source and non-secret instance configuration. A concise default message is `Initialize Nestbox`. If the selected policy requires confirmation or forbids commits, leave the baseline pending and report that clearly.
|
|
251
|
+
|
|
252
|
+
Stage exact intended files only. Preserve unrelated changes. Never commit `.env`, provider credentials, caches, incidental state, or unrelated project files. Never amend, change Git configuration, discard changes, or push without explicit approval.
|
|
253
|
+
|
|
254
|
+
When a super-document intentionally changes files in `/workspace`, follow that workspace repository's own policy separately.
|
|
255
|
+
|
|
256
|
+
## 11. Optional Adaptations
|
|
257
|
+
|
|
258
|
+
Retaining upstream Git history, adding an upstream remote, external page trees, parent-owned engines, submodules, vendoring, shared repositories, alternate Compose layouts, and operating-system shortcuts are optional. Explain their ownership and update consequences and obtain explicit confirmation before creating them. They must not obscure the two default installation paths.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nestbox 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
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Nestbox
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Nestbox is an agent-guided workspace for building focused interfaces around projects, tools, data, and containerized services.
|
|
6
|
+
|
|
7
|
+
Its core design is the **super-document**: one cohesive PHP and Markdown document can provide server logic, command-line behavior, forms, HTML, and browser JavaScript. The internal OpenCode agent creates and extends these documents, integrates project runtimes, and prepares infrastructure changes.
|
|
8
|
+
|
|
9
|
+
Nestbox runs with Docker Compose using Nginx, PHP-FPM, Rollup, and OpenCode. A fresh installation opens with a prompt for creating its first screen instead of shipping a predefined application.
|
|
10
|
+
|
|
11
|
+
## Learn More
|
|
12
|
+
|
|
13
|
+
Copy and send this to an agent with web access:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Learn about Nestbox from https://github.com/piontkovsk11andre1/nestbox. Read its current documentation and explain its super-document design, agent workflow, container integration model, best use cases, and limitations. Do not install or modify anything.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Create a new Nestbox project with npm:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm create @p10i/nestbox@latest
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The creator offers the two default layouts: install directly into the selected directory or install at `<workspace>/.nestbox`. After creation, edit `.env`, then run the Docker Compose validation and startup commands printed by the creator.
|
|
28
|
+
|
|
29
|
+
For agent-guided installation or repair work, copy and send this to an agent with filesystem, Git, Docker, and terminal access:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Install Nestbox by following https://github.com/piontkovsk11andre1/nestbox/blob/main/INSTALL.md completely. Ask all required questions before making changes, preserve unrelated files and Git history, and do not expose secrets.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Update
|
|
36
|
+
|
|
37
|
+
Run an update request from the workspace containing Nestbox, then copy and send this prompt to an agent with filesystem, Git, Docker, and terminal access:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Update this Nestbox installation from https://github.com/piontkovsk11andre1/nestbox. Locate the installation, inspect its local Git history and configuration, read the current remote INSTALL.md and internal Nestbox instructions, preserve pages, instance policy, secrets, volumes, and unrelated changes, explain the proposed update, and ask before destructive or system-level operations. Validate Compose and run the Nestbox host tests after applying the update.
|
|
41
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { cp, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { constants } from 'node:fs';
|
|
9
|
+
|
|
10
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
const packageJson = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'));
|
|
12
|
+
|
|
13
|
+
const copyEntries = [
|
|
14
|
+
'docker',
|
|
15
|
+
'home',
|
|
16
|
+
'tests',
|
|
17
|
+
'docker-compose.yaml',
|
|
18
|
+
'.dockerignore',
|
|
19
|
+
'.env.example',
|
|
20
|
+
'.gitattributes',
|
|
21
|
+
'INSTALL.md',
|
|
22
|
+
'README.md',
|
|
23
|
+
'LICENSE'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const options = {
|
|
28
|
+
target: '',
|
|
29
|
+
layout: '',
|
|
30
|
+
yes: false,
|
|
31
|
+
git: true,
|
|
32
|
+
help: false
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
for (let index = 0; index < argv.length; index++) {
|
|
36
|
+
const arg = argv[index];
|
|
37
|
+
if (arg === '--help' || arg === '-h') options.help = true;
|
|
38
|
+
else if (arg === '--yes' || arg === '-y') options.yes = true;
|
|
39
|
+
else if (arg === '--no-git') options.git = false;
|
|
40
|
+
else if (arg === '--layout') options.layout = argv[++index] || '';
|
|
41
|
+
else if (arg.startsWith('--layout=')) options.layout = arg.slice('--layout='.length);
|
|
42
|
+
else if (arg === '--target') options.target = argv[++index] || '';
|
|
43
|
+
else if (arg.startsWith('--target=')) options.target = arg.slice('--target='.length);
|
|
44
|
+
else if (!arg.startsWith('-') && !options.target) options.target = arg;
|
|
45
|
+
else throw new Error(`Unknown argument: ${arg}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return options;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function printHelp() {
|
|
52
|
+
console.log(`Create a Nestbox project.\n\nUsage:\n npm create @p10i/nestbox@latest [target] [options]\n npx @p10i/create-nestbox [target] [options]\n\nOptions:\n --layout current|nestbox Install directly into target or into target/.nestbox\n --target <path> Target directory\n --yes, -y Use defaults for missing prompts\n --no-git Do not initialize fresh installation Git history\n --help, -h Show this help\n`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function pathExists(path) {
|
|
56
|
+
try {
|
|
57
|
+
await stat(path);
|
|
58
|
+
return true;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error && error.code === 'ENOENT') return false;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function isDirectoryEmpty(path) {
|
|
66
|
+
if (!(await pathExists(path))) return true;
|
|
67
|
+
const entries = await readdir(path);
|
|
68
|
+
return entries.length === 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validateLayout(layout) {
|
|
72
|
+
if (layout !== 'current' && layout !== 'nestbox') {
|
|
73
|
+
throw new Error('Layout must be "current" or "nestbox".');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function ask(question, defaultValue, rl, yes) {
|
|
78
|
+
if (yes) return defaultValue;
|
|
79
|
+
const suffix = defaultValue ? ` (${defaultValue})` : '';
|
|
80
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
81
|
+
return answer || defaultValue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function copyTemplate(installDir) {
|
|
85
|
+
for (const entry of copyEntries) {
|
|
86
|
+
await cp(join(packageRoot, entry), join(installDir, entry), {
|
|
87
|
+
recursive: true,
|
|
88
|
+
errorOnExist: true,
|
|
89
|
+
force: false,
|
|
90
|
+
mode: constants.COPYFILE_FICLONE
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await cp(join(packageRoot, 'template.gitignore'), join(installDir, '.gitignore'), {
|
|
95
|
+
errorOnExist: true,
|
|
96
|
+
force: false
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function configureInstance(installDir, workspaceDir, layout) {
|
|
101
|
+
await cp(join(installDir, '.env.example'), join(installDir, '.env'), {
|
|
102
|
+
errorOnExist: true,
|
|
103
|
+
force: false
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const instanceExample = join(installDir, 'home', 'configs', 'opencode', 'instance.example.md');
|
|
107
|
+
const instancePath = join(installDir, 'home', 'configs', 'opencode', 'instance.md');
|
|
108
|
+
let instance = await readFile(instanceExample, 'utf8');
|
|
109
|
+
|
|
110
|
+
const workspaceDescription = layout === 'nestbox'
|
|
111
|
+
? 'the parent workspace directory'
|
|
112
|
+
: 'the installation directory';
|
|
113
|
+
const layoutDescription = layout === 'nestbox'
|
|
114
|
+
? '.nestbox'
|
|
115
|
+
: 'current directory';
|
|
116
|
+
|
|
117
|
+
instance = instance
|
|
118
|
+
.replace('- Layout: current directory.', `- Layout: ${layoutDescription}.`)
|
|
119
|
+
.replace('- Workspace: `/workspace` maps to the installation directory.', `- Workspace: \`/workspace\` maps to ${workspaceDescription}.`)
|
|
120
|
+
.replace('- Upstream source revision: record during installation.', `- Upstream source revision: npm package ${packageJson.name}@${packageJson.version}.`)
|
|
121
|
+
.replace('- Commit policy: unconfigured; ask before any commit until the installer replaces this line with the user\'s automatic, per-commit confirmation, or no-commit choice.', '- Commit policy: per-commit confirmation; ask before each commit.')
|
|
122
|
+
.trimEnd();
|
|
123
|
+
|
|
124
|
+
instance += `\n\n- Host installation path: ${installDir}\n- Host workspace path: ${workspaceDir}\n`;
|
|
125
|
+
|
|
126
|
+
await writeFile(instancePath, instance, 'utf8');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function commandExists(command) {
|
|
130
|
+
const result = spawnSync(command, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' });
|
|
131
|
+
return result.status === 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function initGit(installDir) {
|
|
135
|
+
if (!commandExists('git')) return false;
|
|
136
|
+
const result = spawnSync('git', ['init'], { cwd: installDir, stdio: 'inherit', shell: process.platform === 'win32' });
|
|
137
|
+
if (result.status !== 0) throw new Error('git init failed.');
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function main() {
|
|
142
|
+
const options = parseArgs(process.argv.slice(2));
|
|
143
|
+
if (options.help) {
|
|
144
|
+
printHelp();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const rl = createInterface({ input, output });
|
|
149
|
+
try {
|
|
150
|
+
const targetInput = options.target || await ask('Target directory', '.', rl, options.yes);
|
|
151
|
+
const layoutInput = options.layout || await ask('Layout: current or nestbox', 'nestbox', rl, options.yes);
|
|
152
|
+
validateLayout(layoutInput);
|
|
153
|
+
|
|
154
|
+
const workspaceDir = resolve(process.cwd(), targetInput);
|
|
155
|
+
const installDir = layoutInput === 'nestbox' ? join(workspaceDir, '.nestbox') : workspaceDir;
|
|
156
|
+
|
|
157
|
+
if (!(await isDirectoryEmpty(installDir))) {
|
|
158
|
+
throw new Error(`Refusing to overwrite non-empty installation target: ${installDir}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await mkdir(installDir, { recursive: true });
|
|
162
|
+
await copyTemplate(installDir);
|
|
163
|
+
await configureInstance(installDir, workspaceDir, layoutInput);
|
|
164
|
+
const gitInitialized = options.git ? initGit(installDir) : false;
|
|
165
|
+
|
|
166
|
+
console.log('\nNestbox project created.');
|
|
167
|
+
console.log(`Installation: ${installDir}`);
|
|
168
|
+
console.log(`Workspace: ${workspaceDir}`);
|
|
169
|
+
console.log(`Git initialized: ${gitInitialized ? 'yes' : 'no'}`);
|
|
170
|
+
console.log('\nNext steps:');
|
|
171
|
+
console.log(` cd ${installDir}`);
|
|
172
|
+
console.log(' Edit .env and set COMPOSE_PROJECT_NAME, WEB_PORT, OpenCode credentials, and provider keys.');
|
|
173
|
+
console.log(' docker compose config --quiet');
|
|
174
|
+
console.log(' docker compose up -d --build');
|
|
175
|
+
console.log(process.platform === 'win32' ? ' .\\tests\\nestbox.ps1' : ' bash tests/nestbox.sh');
|
|
176
|
+
} finally {
|
|
177
|
+
rl.close();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
main().catch((error) => {
|
|
182
|
+
console.error(`create-nestbox: ${error.message}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
|
|
3
|
+
# ---- Stage 1: build the JS/CSS bundles with Rollup ----
|
|
4
|
+
FROM node:alpine AS bundles
|
|
5
|
+
|
|
6
|
+
WORKDIR /sources/rollup
|
|
7
|
+
COPY ./home/configs/rollup/package.json ./package.json
|
|
8
|
+
COPY ./home/configs/rollup/package-lock.json ./package-lock.json
|
|
9
|
+
RUN npm ci --no-audit --no-fund
|
|
10
|
+
|
|
11
|
+
COPY ./home/configs/rollup/rollup.config.js ./rollup.config.js
|
|
12
|
+
COPY ./home/code /home/code
|
|
13
|
+
RUN npx rollup -c
|
|
14
|
+
|
|
15
|
+
# ---- Stage 2: runtime ----
|
|
16
|
+
FROM alpine:3.23
|
|
17
|
+
|
|
18
|
+
RUN apk add --no-cache nginx nginx-mod-http-nchan && \
|
|
19
|
+
mkdir -p /run/nginx /var/lib/nginx/tmp
|
|
20
|
+
|
|
21
|
+
COPY ./home/configs/nginx/nginx.conf /etc/nginx/nginx.conf
|
|
22
|
+
COPY ./home/code /var/www/html
|
|
23
|
+
# Overwrite the source bundles with the freshly built ones from stage 1.
|
|
24
|
+
COPY --from=bundles /home/code/scripts-bundle.js /var/www/html/scripts-bundle.js
|
|
25
|
+
COPY --from=bundles /home/code/scripts-bundle.css /var/www/html/scripts-bundle.css
|
|
26
|
+
|
|
27
|
+
EXPOSE 80 8080
|
|
28
|
+
|
|
29
|
+
STOPSIGNAL SIGQUIT
|
|
30
|
+
|
|
31
|
+
CMD ["nginx", "-g", "daemon off;"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
FROM composer:latest AS vendor
|
|
2
|
+
|
|
3
|
+
WORKDIR /build
|
|
4
|
+
COPY ./home/configs/php/composer.json ./composer.json
|
|
5
|
+
COPY ./home/configs/php/composer.lock ./composer.lock
|
|
6
|
+
RUN composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
|
|
7
|
+
|
|
8
|
+
FROM ghcr.io/anomalyco/opencode:latest
|
|
9
|
+
|
|
10
|
+
ARG APP_UID=1000
|
|
11
|
+
ARG APP_GID=1000
|
|
12
|
+
|
|
13
|
+
RUN apk add --no-cache git php85 php85-dom php85-mbstring php85-openssl php85-phar php85-tokenizer php85-xml php85-xmlwriter su-exec && \
|
|
14
|
+
ln -s /usr/bin/php85 /usr/local/bin/php && \
|
|
15
|
+
addgroup -g "${APP_GID}" -S nestbox && \
|
|
16
|
+
adduser -u "${APP_UID}" -G nestbox -S -h /home/nestbox nestbox
|
|
17
|
+
|
|
18
|
+
ENV OPENCODE_CONFIG=/etc/opencode/opencode.json
|
|
19
|
+
ENV HOME=/home/nestbox
|
|
20
|
+
|
|
21
|
+
COPY --from=vendor /build/vendor /vendor
|
|
22
|
+
COPY ./home/configs/php /sources/php
|
|
23
|
+
COPY ./home/configs/opencode /etc/opencode
|
|
24
|
+
COPY ./home/code /home/code
|
|
25
|
+
COPY ./docker/opencode/entrypoint.sh /usr/local/bin/nestbox-opencode-entrypoint
|
|
26
|
+
|
|
27
|
+
RUN chmod 0755 /usr/local/bin/nestbox-opencode-entrypoint && \
|
|
28
|
+
mkdir -p /home/nestbox/.local/share/opencode && \
|
|
29
|
+
chown -R nestbox:nestbox /home/nestbox /home/code
|
|
30
|
+
|
|
31
|
+
WORKDIR /workspace
|
|
32
|
+
|
|
33
|
+
EXPOSE 4096
|
|
34
|
+
|
|
35
|
+
ENTRYPOINT ["/usr/local/bin/nestbox-opencode-entrypoint"]
|
|
36
|
+
CMD ["serve", "--hostname", "0.0.0.0", "--port", "4096"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
# Named volumes retain ownership across image recreation. Repair only OpenCode's
|
|
5
|
+
# private state volume before dropping privileges; never chown host bind mounts.
|
|
6
|
+
mkdir -p "$HOME/.local/share/opencode"
|
|
7
|
+
chown -R nestbox:nestbox "$HOME/.local/share/opencode"
|
|
8
|
+
|
|
9
|
+
exec su-exec nestbox:nestbox opencode "$@"
|