@kortix/sandbox 0.4.1 → 0.4.3
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/config/customize.sh +92 -105
- package/config/kortix-env-setup.sh +5 -5
- package/kortix-master/src/index.ts +12 -2
- package/kortix-master/src/routes/update.ts +23 -66
- package/opencode/skills/KORTIX-cron-triggers/SKILL.md +19 -19
- package/opencode/skills/KORTIX-deploy/SKILL.md +438 -0
- package/opencode/skills/KORTIX-deploy/references/freestyle-api.md +279 -0
- package/package.json +1 -1
- package/patch-agent-browser.js +10 -3
- package/services/KORTIX-presentation-viewer/run +0 -37
- package/services/agent-browser-viewer/run +0 -48
- package/services/kortix-master/run +0 -16
- package/services/lss-sync/run +0 -22
- package/services/opencode-serve/run +0 -25
- package/services/opencode-web/run +0 -21
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Freestyle Serverless Deployments - Full API Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for `freestyle.serverless.deployments.create()`.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { freestyle, readFiles } from "freestyle-sandboxes"; // readFiles for local file deploys
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires `FREESTYLE_API_KEY` environment variable.
|
|
12
|
+
|
|
13
|
+
## Source Types (exactly one required)
|
|
14
|
+
|
|
15
|
+
### Git Repository
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
{
|
|
19
|
+
repo: "https://github.com/user/repo", // public URL, authenticated URL, or freestyle repo ID
|
|
20
|
+
branch: "main", // optional, defaults to default branch
|
|
21
|
+
rootPath: "./apps/web", // optional, for monorepos
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Private repos: use `https://user:token@github.com/user/repo.git` or a Freestyle Git repo ID.
|
|
26
|
+
|
|
27
|
+
### Inline Code
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
{
|
|
31
|
+
code: `
|
|
32
|
+
import express from 'express';
|
|
33
|
+
const app = express();
|
|
34
|
+
app.get('/', (req, res) => res.send('Hello'));
|
|
35
|
+
app.listen(3000);
|
|
36
|
+
`,
|
|
37
|
+
nodeModules: { // required with code deploys
|
|
38
|
+
express: "^4.18.2",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Local Files (readFiles)
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { readFiles } from "freestyle-sandboxes"; // NOT from /utils
|
|
47
|
+
|
|
48
|
+
const files = await readFiles("./dist"); // reads dir, excludes node_modules, base64-encodes
|
|
49
|
+
{
|
|
50
|
+
files,
|
|
51
|
+
entrypointPath: "server.js", // required for file deploys - MUST be a Node.js server, not an HTML file
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`readFiles` automatically excludes `node_modules/` and handles binary files. The `entrypointPath` MUST point to a Node.js server file (e.g., Express/Hono) — pointing it at an HTML file will not serve sub-assets.
|
|
56
|
+
|
|
57
|
+
### Tar URL
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
{
|
|
61
|
+
tarUrl: "https://s3.example.com/signed-url/app.tar.gz",
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Useful when source is hosted externally (S3, GCS, etc).
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
### domains (required)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
domains: ["my-app.style.dev"] // free *.style.dev subdomain
|
|
73
|
+
domains: ["app.style.dev", "app.yourdomain.com"] // multiple domains
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Any unclaimed `*.style.dev` subdomain works instantly with SSL. Custom domains need verification first.
|
|
77
|
+
|
|
78
|
+
### build
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
build: true // auto-detect framework, run build
|
|
82
|
+
build: {
|
|
83
|
+
command: "npm run build", // custom build command
|
|
84
|
+
outDir: "dist", // build output directory
|
|
85
|
+
envVars: { // build-time env vars (NOT available at runtime)
|
|
86
|
+
NEXT_PUBLIC_API_URL: "https://api.example.com",
|
|
87
|
+
NODE_ENV: "production",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Default: `false` (deploy files as-is). Set `true` for Next.js, Vite, and other frameworks requiring a build step. Freestyle auto-detects Next.js, Vite, and Expo.
|
|
93
|
+
|
|
94
|
+
### entrypoint
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
entrypoint: "server.js" // main file of your application
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Auto-detected for Next.js, Vite, Expo. Specify manually for custom setups.
|
|
101
|
+
|
|
102
|
+
### envVars
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
envVars: {
|
|
106
|
+
API_KEY: "secret-value", // available at RUNTIME only
|
|
107
|
+
DATABASE_URL: "postgres://...",
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
NOT available at build time. For build-time vars, use `build.envVars`. Env vars are tied to deployments; to change them, create a new deployment.
|
|
112
|
+
|
|
113
|
+
### nodeModules
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
nodeModules: {
|
|
117
|
+
express: "^4.18.2",
|
|
118
|
+
cors: "^2.8.5",
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Only needed for `code` deploys. For git/file deploys, include your lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lock`) and Freestyle installs from it.
|
|
123
|
+
|
|
124
|
+
### timeoutMs
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
timeoutMs: 60000 // milliseconds, idle timeout before scale-down
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Timeout is from last TCP packet (not HTTP request), so WebSockets stay alive as long as you ping faster than the timeout.
|
|
131
|
+
|
|
132
|
+
### networkPermissions
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
networkPermissions: [
|
|
136
|
+
{ action: "allow", domain: "api.stripe.com", behavior: "exact" },
|
|
137
|
+
{ action: "allow", domain: ".*\\.amazonaws\\.com", behavior: "regex" },
|
|
138
|
+
]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### await
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
await: false // return immediately with deploymentId for polling
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Default: `true` (waits for deployment to build and propagate). Set `false` to return immediately.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Polling pattern
|
|
151
|
+
const { deploymentId } = await freestyle.serverless.deployments.create({
|
|
152
|
+
...,
|
|
153
|
+
await: false,
|
|
154
|
+
});
|
|
155
|
+
const status = await freestyle.serverless.deployments.get({ deploymentId });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### waitForRollout
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
waitForRollout: true // wait until deployment is fully serving traffic
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### staticOnly
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
staticOnly: true,
|
|
168
|
+
publicDir: "dist", // directory with static files
|
|
169
|
+
cleanUrls: true, // /about.html becomes /about
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Serves files directly without a server entrypoint. For pure static sites.
|
|
173
|
+
|
|
174
|
+
### headers
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
headers: [
|
|
178
|
+
{
|
|
179
|
+
source: "^/assets/.*$",
|
|
180
|
+
headers: [{ key: "Cache-Control", value: "max-age=31536000, immutable" }],
|
|
181
|
+
},
|
|
182
|
+
]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### redirects
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
redirects: [
|
|
189
|
+
{ source: "^/old-page$", destination: "/new-page", permanent: true },
|
|
190
|
+
]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Return Value
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const { deployment, domains } = await freestyle.serverless.deployments.create({...});
|
|
197
|
+
|
|
198
|
+
deployment.deploymentId // string - unique deployment ID
|
|
199
|
+
domains // string[] - live URLs
|
|
200
|
+
|
|
201
|
+
// With await: false
|
|
202
|
+
const { deploymentId } = await freestyle.serverless.deployments.create({..., await: false});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Custom Domains
|
|
206
|
+
|
|
207
|
+
### Step 1: Verify ownership
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const { record, instructions } = await freestyle.domains.verifications.create({
|
|
211
|
+
domain: "example.com",
|
|
212
|
+
});
|
|
213
|
+
// Add TXT record: _freestyle_custom_hostname.example.com → <verification-code>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Step 2: Complete verification
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
await freestyle.domains.verifications.complete({ domain: "example.com" });
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Step 3: Configure DNS
|
|
223
|
+
|
|
224
|
+
Point domain to Freestyle:
|
|
225
|
+
- **APEX** (`example.com`): A record → `35.235.84.134`
|
|
226
|
+
- **Subdomain** (`app.example.com`): A record for `app` → `35.235.84.134`
|
|
227
|
+
- **Wildcard** (`*.example.com`): A record for `*` → `35.235.84.134`
|
|
228
|
+
|
|
229
|
+
### Step 4: Deploy
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
await freestyle.serverless.deployments.create({
|
|
233
|
+
...,
|
|
234
|
+
domains: ["example.com"],
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Framework Notes
|
|
239
|
+
|
|
240
|
+
### Next.js
|
|
241
|
+
|
|
242
|
+
Requires in `next.config.mjs`:
|
|
243
|
+
```javascript
|
|
244
|
+
const nextConfig = {
|
|
245
|
+
output: "standalone", // required
|
|
246
|
+
images: { unoptimized: true }, // required (no Sharp on Freestyle)
|
|
247
|
+
};
|
|
248
|
+
export default nextConfig;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
For local file deploys, after `npm run build`:
|
|
252
|
+
```bash
|
|
253
|
+
cp -r public .next/standalone/public
|
|
254
|
+
cp -r .next/static .next/standalone/.next/static
|
|
255
|
+
cp package-lock.json .next/standalone/package-lock.json
|
|
256
|
+
```
|
|
257
|
+
Then `readFiles(".next/standalone")` with `entrypointPath: "server.js"`.
|
|
258
|
+
|
|
259
|
+
### Vite
|
|
260
|
+
|
|
261
|
+
Auto-detected when deploying from git with `build: true`. For local file deploys, use an Express static server:
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
import express from 'express';
|
|
265
|
+
import { fileURLToPath } from 'url';
|
|
266
|
+
import { dirname, join } from 'path';
|
|
267
|
+
|
|
268
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
269
|
+
const app = express();
|
|
270
|
+
app.use(express.static(__dirname));
|
|
271
|
+
app.get('*', (req, res) => res.sendFile(join(__dirname, 'index.html'))); // SPA fallback
|
|
272
|
+
app.listen(3000);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
> **WARNING**: Do NOT use `Deno.serve()` or Hono's `app.fire()`. Freestyle runs Node.js. Use `app.listen(3000)` (Express) or `serve({ fetch: app.fetch, port: 3000 })` from `@hono/node-server`.
|
|
276
|
+
|
|
277
|
+
### Static Sites
|
|
278
|
+
|
|
279
|
+
Static sites MUST have an Express/Hono Node.js server entrypoint. Use the same Express pattern as Vite above.
|
package/package.json
CHANGED
package/patch-agent-browser.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
|
|
4
|
+
// Auto-detect npm global modules directory
|
|
5
|
+
const npmGlobalRoot = execSync('npm root -g').toString().trim();
|
|
6
|
+
console.log('npm global root:', npmGlobalRoot);
|
|
7
|
+
|
|
8
|
+
const agentBrowserDir = npmGlobalRoot + '/agent-browser/dist';
|
|
2
9
|
|
|
3
10
|
// ── Patch 1: handleLaunch env var fallbacks ─────────────────────────────────
|
|
4
11
|
// The Rust CLI sends a launch command to the Node.js daemon but doesn't
|
|
5
12
|
// include executablePath/args/profile in the JSON. We inject env var fallbacks.
|
|
6
|
-
const actionsFile = '/
|
|
13
|
+
const actionsFile = agentBrowserDir + '/actions.js';
|
|
7
14
|
let actions = fs.readFileSync(actionsFile, 'utf8');
|
|
8
15
|
|
|
9
16
|
const oldLaunch = [
|
|
@@ -44,7 +51,7 @@ console.log('PATCH 2 SKIPPED - upstream already allows localhost origins');
|
|
|
44
51
|
// When AGENT_BROWSER_STREAM_PORT is set globally, ALL sessions try to bind
|
|
45
52
|
// that port. Named sessions crash with EADDRINUSE. Fix: only the default
|
|
46
53
|
// session uses the env var port; named sessions use the hash-based port.
|
|
47
|
-
const daemonFile = '/
|
|
54
|
+
const daemonFile = agentBrowserDir + '/daemon.js';
|
|
48
55
|
let daemon = fs.readFileSync(daemonFile, 'utf8');
|
|
49
56
|
|
|
50
57
|
const oldStreamPort = `const streamPort = options?.streamPort ??
|
|
@@ -85,7 +92,7 @@ console.log('PATCH 4 OK - auto-launch profile only for default session');
|
|
|
85
92
|
// When using launchPersistentContext (profile mode), this.browser is null.
|
|
86
93
|
// newTab() checks `!this.browser` and throws "Browser not launched".
|
|
87
94
|
// Fix: also check this.contexts.length > 0 as an alternative.
|
|
88
|
-
const browserFile = '/
|
|
95
|
+
const browserFile = agentBrowserDir + '/browser.js';
|
|
89
96
|
let browser = fs.readFileSync(browserFile, 'utf8');
|
|
90
97
|
|
|
91
98
|
const oldNewTabCheck = "if (!this.browser || this.contexts.length === 0) {\n throw new Error('Browser not launched');\n }\n // Invalidate CDP session since we're switching to a new page";
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# KORTIX Presentation Viewer — s6 service
|
|
3
|
-
#
|
|
4
|
-
# Watches for presentations in /workspace/presentations/ and serves
|
|
5
|
-
# the most recently updated one on port 3210.
|
|
6
|
-
# If no presentation exists yet, waits until one appears.
|
|
7
|
-
|
|
8
|
-
PRES_ROOT="/workspace/presentations"
|
|
9
|
-
VIEWER="/opt/opencode/skills/KORTIX-presentation-viewer/serve.ts"
|
|
10
|
-
PORT=3210
|
|
11
|
-
|
|
12
|
-
wait_for_presentation() {
|
|
13
|
-
while true; do
|
|
14
|
-
if [ -d "$PRES_ROOT" ]; then
|
|
15
|
-
LATEST=$(find "$PRES_ROOT" -maxdepth 2 -name "metadata.json" -newer /tmp/.pv-last-check 2>/dev/null | head -1)
|
|
16
|
-
if [ -z "$LATEST" ]; then
|
|
17
|
-
LATEST=$(find "$PRES_ROOT" -maxdepth 2 -name "metadata.json" 2>/dev/null | head -1)
|
|
18
|
-
fi
|
|
19
|
-
if [ -n "$LATEST" ]; then
|
|
20
|
-
dirname "$LATEST"
|
|
21
|
-
return 0
|
|
22
|
-
fi
|
|
23
|
-
fi
|
|
24
|
-
sleep 3
|
|
25
|
-
done
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
touch /tmp/.pv-last-check
|
|
29
|
-
|
|
30
|
-
echo "[KORTIX-pv] Waiting for a presentation in $PRES_ROOT ..."
|
|
31
|
-
PRES_DIR=$(wait_for_presentation)
|
|
32
|
-
echo "[KORTIX-pv] Serving: $PRES_DIR on port $PORT"
|
|
33
|
-
|
|
34
|
-
touch /tmp/.pv-last-check
|
|
35
|
-
|
|
36
|
-
exec s6-setuidgid abc \
|
|
37
|
-
/usr/local/bin/bun run "$VIEWER" "$PRES_DIR"
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# Agent Browser Viewer — s6 service
|
|
3
|
-
#
|
|
4
|
-
# Serves the browser viewer UI on port 9224.
|
|
5
|
-
# - GET / → viewer HTML
|
|
6
|
-
# - GET /sessions → JSON list of active sessions + stream ports
|
|
7
|
-
|
|
8
|
-
echo "[agent-browser-viewer] Starting viewer on port 9224"
|
|
9
|
-
|
|
10
|
-
exec s6-setuidgid abc \
|
|
11
|
-
node -e '
|
|
12
|
-
const http = require("http");
|
|
13
|
-
const fs = require("fs");
|
|
14
|
-
const path = require("path");
|
|
15
|
-
|
|
16
|
-
const html = fs.readFileSync("/opt/agent-browser-viewer/index.html");
|
|
17
|
-
const socketDir = process.env.AGENT_BROWSER_SOCKET_DIR || "/workspace/.agent-browser";
|
|
18
|
-
|
|
19
|
-
function getSessions() {
|
|
20
|
-
try {
|
|
21
|
-
const files = fs.readdirSync(socketDir);
|
|
22
|
-
const sessions = [];
|
|
23
|
-
for (const f of files) {
|
|
24
|
-
if (f.endsWith(".stream")) {
|
|
25
|
-
const name = f.replace(".stream", "");
|
|
26
|
-
const port = parseInt(fs.readFileSync(path.join(socketDir, f), "utf8").trim(), 10);
|
|
27
|
-
const hasSock = files.includes(name + ".sock");
|
|
28
|
-
if (port > 0 && hasSock) {
|
|
29
|
-
sessions.push({ name, port });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return sessions;
|
|
34
|
-
} catch(e) { return []; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
http.createServer((req, res) => {
|
|
38
|
-
if (req.url === "/sessions") {
|
|
39
|
-
res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache" });
|
|
40
|
-
res.end(JSON.stringify(getSessions()));
|
|
41
|
-
} else {
|
|
42
|
-
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-cache" });
|
|
43
|
-
res.end(html);
|
|
44
|
-
}
|
|
45
|
-
}).listen(9224, "0.0.0.0", () => {
|
|
46
|
-
console.log("[agent-browser-viewer] Ready at http://0.0.0.0:9224");
|
|
47
|
-
});
|
|
48
|
-
'
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# Kortix Master — s6 service
|
|
3
|
-
#
|
|
4
|
-
# Reverse proxy that sits in front of OpenCode on port 8000.
|
|
5
|
-
# All external traffic enters through Kortix Master, which proxies
|
|
6
|
-
# to the OpenCode API server on localhost:4096.
|
|
7
|
-
|
|
8
|
-
echo "[kortix-master] Starting on port 8000, proxying to OpenCode at localhost:4096"
|
|
9
|
-
|
|
10
|
-
exec s6-setuidgid abc \
|
|
11
|
-
env HOME=/workspace \
|
|
12
|
-
KORTIX_MASTER_PORT=8000 \
|
|
13
|
-
OPENCODE_HOST=localhost \
|
|
14
|
-
OPENCODE_PORT=4096 \
|
|
15
|
-
PATH="/opt/bun/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
16
|
-
/opt/bun/bin/bun run /opt/kortix-master/src/index.ts
|
package/services/lss-sync/run
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# lss-sync — File-watcher daemon that keeps the semantic search index in sync.
|
|
3
|
-
#
|
|
4
|
-
# Uses watchdog (inotify/FSEvents) to detect file changes in real time and
|
|
5
|
-
# triggers re-indexing with debounce. Changes are picked up within seconds.
|
|
6
|
-
#
|
|
7
|
-
# Watches:
|
|
8
|
-
# /workspace — User files and projects
|
|
9
|
-
# /workspace/.kortix — Agent memory
|
|
10
|
-
|
|
11
|
-
exec s6-setuidgid abc \
|
|
12
|
-
env HOME=/workspace \
|
|
13
|
-
OPENAI_API_KEY="${OPENAI_API_KEY}" \
|
|
14
|
-
LSS_DIR=/workspace/.lss \
|
|
15
|
-
PATH="/lsiopy/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
16
|
-
lss-sync \
|
|
17
|
-
--watch /workspace \
|
|
18
|
-
--exclude node_modules \
|
|
19
|
-
--exclude .git \
|
|
20
|
-
--exclude __pycache__ \
|
|
21
|
-
--startup-delay 15 \
|
|
22
|
-
--debounce 2
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# OpenCode API Server — s6 service
|
|
3
|
-
#
|
|
4
|
-
# Runs `opencode serve` on port 4096 (internal only, proxied by kortix-master).
|
|
5
|
-
# This is the headless API server, distinct from `opencode web` (the Web UI).
|
|
6
|
-
|
|
7
|
-
# Wait a moment for filesystem to settle
|
|
8
|
-
sleep 3
|
|
9
|
-
|
|
10
|
-
cd /workspace
|
|
11
|
-
|
|
12
|
-
# Ensure OpenCode data dirs exist and are owned by abc (guards against
|
|
13
|
-
# Daytona race conditions where /workspace ownership may shift after startup.sh)
|
|
14
|
-
mkdir -p /workspace/.local/share/opencode/log \
|
|
15
|
-
/workspace/.local/share/opencode/storage \
|
|
16
|
-
/workspace/.local/share/opencode/snapshot
|
|
17
|
-
chown -R abc:abc /workspace/.local/share/opencode
|
|
18
|
-
|
|
19
|
-
echo "[opencode-serve] Starting OpenCode API server on port 4096"
|
|
20
|
-
|
|
21
|
-
exec s6-setuidgid abc \
|
|
22
|
-
env HOME=/workspace \
|
|
23
|
-
OPENCODE_CONFIG_DIR=/opt/opencode \
|
|
24
|
-
PATH="/opt/bun/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
25
|
-
opencode serve --port 4096 --hostname 0.0.0.0
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/with-contenv bash
|
|
2
|
-
# OpenCode Web UI — serves the web interface on port 3111
|
|
3
|
-
#
|
|
4
|
-
# Runs `opencode web` headless so it's accessible via the browser at
|
|
5
|
-
# http://localhost:3111 (mapped from the container).
|
|
6
|
-
|
|
7
|
-
cd /workspace
|
|
8
|
-
|
|
9
|
-
# Ensure OpenCode data dirs exist and are owned by abc
|
|
10
|
-
mkdir -p /workspace/.local/share/opencode/log \
|
|
11
|
-
/workspace/.local/share/opencode/storage \
|
|
12
|
-
/workspace/.local/share/opencode/snapshot
|
|
13
|
-
chown -R abc:abc /workspace/.local/share/opencode
|
|
14
|
-
|
|
15
|
-
exec s6-setuidgid abc \
|
|
16
|
-
env HOME=/workspace \
|
|
17
|
-
OPENCODE_CONFIG_DIR=/opt/opencode \
|
|
18
|
-
OPENCODE_SERVER_USERNAME= \
|
|
19
|
-
OPENCODE_SERVER_PASSWORD= \
|
|
20
|
-
PATH="/opt/bun/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
21
|
-
opencode web --port 3111 --hostname 0.0.0.0
|