@kitlangton/tailcode 0.1.0 → 0.2.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/LICENSE +21 -0
- package/README.md +75 -1
- package/bin/tailcode.ts +81 -1
- package/bunfig.toml +1 -0
- package/package.json +15 -6
- package/src/app.tsx +3 -1
- package/src/main.tsx +5 -0
- package/tsconfig.json +11 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kit Langton
|
|
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
|
@@ -41,6 +41,34 @@ opencode --version
|
|
|
41
41
|
|
|
42
42
|
## Run TailCode
|
|
43
43
|
|
|
44
|
+
### Option 1: Binary Releases (Recommended)
|
|
45
|
+
|
|
46
|
+
Download pre-built binaries from [GitHub Releases](https://github.com/kitlangton/tailcode/releases):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# macOS (Apple Silicon)
|
|
50
|
+
curl -L -o tailcode https://github.com/kitlangton/tailcode/releases/latest/download/tailcode-darwin-arm64
|
|
51
|
+
chmod +x tailcode
|
|
52
|
+
./tailcode
|
|
53
|
+
|
|
54
|
+
# macOS (Intel)
|
|
55
|
+
curl -L -o tailcode https://github.com/kitlangton/tailcode/releases/latest/download/tailcode-darwin-x64
|
|
56
|
+
chmod +x tailcode
|
|
57
|
+
./tailcode
|
|
58
|
+
|
|
59
|
+
# Linux (x64)
|
|
60
|
+
curl -L -o tailcode https://github.com/kitlangton/tailcode/releases/latest/download/tailcode-linux-x64
|
|
61
|
+
chmod +x tailcode
|
|
62
|
+
./tailcode
|
|
63
|
+
|
|
64
|
+
# Linux (ARM64)
|
|
65
|
+
curl -L -o tailcode https://github.com/kitlangton/tailcode/releases/latest/download/tailcode-linux-arm64
|
|
66
|
+
chmod +x tailcode
|
|
67
|
+
./tailcode
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Option 2: Via Bun (requires Bun runtime)
|
|
71
|
+
|
|
44
72
|
Requires Bun (the `tailcode` executable is a Bun CLI):
|
|
45
73
|
|
|
46
74
|
```bash
|
|
@@ -60,7 +88,7 @@ bun add -g @kitlangton/tailcode
|
|
|
60
88
|
tailcode
|
|
61
89
|
```
|
|
62
90
|
|
|
63
|
-
Run from
|
|
91
|
+
### Option 3: Run from Source
|
|
64
92
|
|
|
65
93
|
```bash
|
|
66
94
|
bun install
|
|
@@ -88,4 +116,50 @@ TAILCODE_PORT=4096 TAILCODE_PASSWORD=secret bun run start
|
|
|
88
116
|
|
|
89
117
|
- The published URL is only reachable from devices on your Tailscale tailnet
|
|
90
118
|
- OpenCode is bound to localhost to avoid exposing it on your LAN
|
|
119
|
+
- Running `tailcode` again will auto-attach if OpenCode is already running locally
|
|
91
120
|
- TailCode shows a local attach command after setup: `opencode attach http://127.0.0.1:4096`
|
|
121
|
+
|
|
122
|
+
## Binary Releases
|
|
123
|
+
|
|
124
|
+
We provide standalone binaries for:
|
|
125
|
+
|
|
126
|
+
- **macOS**: `arm64` (Apple Silicon), `x64` (Intel)
|
|
127
|
+
- **Linux**: `x64`, `arm64`
|
|
128
|
+
- **Windows**: `x64` (coming soon)
|
|
129
|
+
|
|
130
|
+
Binaries are compiled with Bun and include the Bun runtime. No separate Bun installation needed.
|
|
131
|
+
|
|
132
|
+
### Verification
|
|
133
|
+
|
|
134
|
+
All releases include SHA256 checksums in `SHA256SUMS`. Verify after download:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# macOS/Linux
|
|
138
|
+
sha256sum -c SHA256SUMS
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Development
|
|
142
|
+
|
|
143
|
+
### Building Locally
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Bundle for local testing
|
|
147
|
+
bun run build:bundle
|
|
148
|
+
|
|
149
|
+
# Compile for current platform
|
|
150
|
+
bun run build:compile
|
|
151
|
+
|
|
152
|
+
# Full release build (all platforms + checksums)
|
|
153
|
+
bun run build:release
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Scripts
|
|
157
|
+
|
|
158
|
+
- `bun run typecheck` - Type check with TypeScript
|
|
159
|
+
- `bun run lint` - Lint with oxlint
|
|
160
|
+
- `bun run fmt` - Format with oxfmt
|
|
161
|
+
- `bun run check` - Run all checks (typecheck + lint + fmt)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
package/bin/tailcode.ts
CHANGED
|
@@ -1,3 +1,83 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export {}
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PORT = 4096
|
|
6
|
+
|
|
7
|
+
function printHelp() {
|
|
8
|
+
process.stdout.write(
|
|
9
|
+
`tailcode\n\nUsage:\n tailcode [--wizard] [--attach] [--help]\n\nOptions:\n --wizard Always open the TailCode setup wizard\n --attach Attach to an already-running local OpenCode server\n --help Show this help\n`,
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolvePort() {
|
|
14
|
+
const raw = process.env.TAILCODE_PORT
|
|
15
|
+
if (!raw) return DEFAULT_PORT
|
|
16
|
+
const parsed = Number.parseInt(raw, 10)
|
|
17
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : DEFAULT_PORT
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function isHealthy(port: number) {
|
|
21
|
+
const controller = new AbortController()
|
|
22
|
+
const timer = setTimeout(() => controller.abort(), 600)
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`http://127.0.0.1:${port}/global/health`, {
|
|
25
|
+
signal: controller.signal,
|
|
26
|
+
})
|
|
27
|
+
return response.ok
|
|
28
|
+
} catch {
|
|
29
|
+
return false
|
|
30
|
+
} finally {
|
|
31
|
+
clearTimeout(timer)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function runAttach(port: number) {
|
|
36
|
+
const bin = Bun.which("opencode")
|
|
37
|
+
if (!bin) {
|
|
38
|
+
process.stderr.write("tailcode: 'opencode' is not installed (launching wizard instead)\n")
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const target = `http://127.0.0.1:${port}`
|
|
43
|
+
process.stdout.write(`tailcode: attaching to ${target}\n`)
|
|
44
|
+
|
|
45
|
+
const child = Bun.spawn([bin, "attach", target], {
|
|
46
|
+
stdin: "inherit",
|
|
47
|
+
stdout: "inherit",
|
|
48
|
+
stderr: "inherit",
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
process.exit(await child.exited)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const args = process.argv.slice(2)
|
|
55
|
+
const forceWizard = args.includes("--wizard")
|
|
56
|
+
const forceAttach = args.includes("--attach")
|
|
57
|
+
|
|
58
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
59
|
+
printHelp()
|
|
60
|
+
process.exit(0)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (forceWizard && forceAttach) {
|
|
64
|
+
process.stderr.write("tailcode: use either --wizard or --attach, not both\n")
|
|
65
|
+
process.exit(1)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const port = resolvePort()
|
|
69
|
+
|
|
70
|
+
if (!forceWizard) {
|
|
71
|
+
const healthy = await isHealthy(port)
|
|
72
|
+
if (healthy) {
|
|
73
|
+
await runAttach(port)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (forceAttach) {
|
|
77
|
+
process.stderr.write(`tailcode: OpenCode is not running on http://127.0.0.1:${port}\n`)
|
|
78
|
+
process.stderr.write("tailcode: run without --attach to start the setup wizard\n")
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await import("../src/main.tsx")
|
package/bunfig.toml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
preload = ["@opentui/solid/preload"]
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitlangton/tailcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Terminal wizard for publishing OpenCode to your Tailscale tailnet",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"src/qr.ts",
|
|
32
32
|
"src/state.ts",
|
|
33
33
|
"src/services/*.ts",
|
|
34
|
+
"tsconfig.json",
|
|
35
|
+
"bunfig.toml",
|
|
34
36
|
"README.md"
|
|
35
37
|
],
|
|
36
38
|
"bin": {
|
|
@@ -45,10 +47,17 @@
|
|
|
45
47
|
"showcase": "bun run --conditions=browser --preserve-symlinks src/showcase.tsx",
|
|
46
48
|
"showcase:dev": "bun run --conditions=browser --preserve-symlinks --hot src/showcase.tsx",
|
|
47
49
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
48
|
-
"fmt": "oxfmt --write src",
|
|
49
|
-
"fmt:check": "oxfmt --check src",
|
|
50
|
-
"lint": "oxlint src",
|
|
51
|
-
"check": "bun run typecheck && bun run lint && bun run fmt:check"
|
|
50
|
+
"fmt": "oxfmt --write src bin",
|
|
51
|
+
"fmt:check": "oxfmt --check src bin",
|
|
52
|
+
"lint": "oxlint src bin",
|
|
53
|
+
"check": "bun run typecheck && bun run lint && bun run fmt:check",
|
|
54
|
+
"build": "bun run build.ts",
|
|
55
|
+
"build:bundle": "bun run build.ts bundle",
|
|
56
|
+
"build:compile": "bun run build.ts compile",
|
|
57
|
+
"build:release": "bun run build.ts release",
|
|
58
|
+
"smoke:bundle": "bun run ./dist/tailcode.js",
|
|
59
|
+
"publish:dry": "bun run check && bun run build && bun publish --dry-run",
|
|
60
|
+
"publish:npm": "bun run check && bun run build && bun publish"
|
|
52
61
|
},
|
|
53
62
|
"dependencies": {
|
|
54
63
|
"@effect/atom-solid": "4.0.0-beta.11",
|
package/src/app.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
1
3
|
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
|
|
2
4
|
import { TextAttributes } from "@opentui/core"
|
|
3
5
|
import { createMemo, createSignal, onCleanup, onMount, Show } from "solid-js"
|
|
@@ -409,7 +411,7 @@ export function App(props: AppProps = {}) {
|
|
|
409
411
|
}
|
|
410
412
|
|
|
411
413
|
const stageRow = () => (
|
|
412
|
-
<box flexDirection=
|
|
414
|
+
<box flexDirection="row" gap={0}>
|
|
413
415
|
{stageBadge("tailscale", "Tailscale")}
|
|
414
416
|
{stageBadge("opencode", "OpenCode")}
|
|
415
417
|
{stageBadge("publish", "Publish")}
|
package/src/main.tsx
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
// Explicitly load the OpenTUI Solid preload (normally done via bunfig.toml in dev)
|
|
4
|
+
import "@opentui/solid/preload"
|
|
5
|
+
|
|
1
6
|
import { render } from "@opentui/solid"
|
|
2
7
|
import { RegistryContext } from "@effect/atom-solid/RegistryContext"
|
|
3
8
|
import { App } from "./app.js"
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"extends": "@tsconfig/bun/tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"jsx": "preserve",
|
|
6
|
+
"jsxImportSource": "@opentui/solid",
|
|
7
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
8
|
+
"plugins": [{ "name": "@effect/language-service" }]
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.ts", "src/**/*.tsx", "bin/**/*.ts"]
|
|
11
|
+
}
|