@work-bridge/work-bridge 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/LICENSE +21 -0
- package/README.md +358 -0
- package/bin/work-bridge.js +15 -0
- package/package.json +42 -0
- package/scripts/install.cjs +105 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jaeyoung0509
|
|
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,358 @@
|
|
|
1
|
+
# work-bridge
|
|
2
|
+
|
|
3
|
+
> **Switching between Claude Code, Gemini CLI, OpenCode, and Codex due to LLM cost?**
|
|
4
|
+
> Bring your session context, MCP configs, and skills with you — instantly.
|
|
5
|
+
|
|
6
|
+
`work-bridge` is a local-first portability layer for AI coding-agent workflows. It inspects your current sessions, skills, and MCP server configs across all major tools, then exports a portable bundle that any other tool can pick up from where you left off.
|
|
7
|
+
|
|
8
|
+
[](https://golang.org)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](https://www.npmjs.com/package/@work-bridge/work-bridge)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Why work-bridge?
|
|
15
|
+
|
|
16
|
+
Modern AI coding agents store their state in incompatible formats:
|
|
17
|
+
|
|
18
|
+
| What you lose when switching tools | What work-bridge preserves |
|
|
19
|
+
|------------------------------------|---------------------------|
|
|
20
|
+
| Current task title & goal | ✅ Task title & current goal |
|
|
21
|
+
| Session summary & decisions | ✅ Summary, decisions, failures |
|
|
22
|
+
| Project instruction files (AGENTS.md, CLAUDE.md, GEMINI.md…) | ✅ All instruction artifacts |
|
|
23
|
+
| MCP server configurations | ✅ Inspected & validated MCP configs |
|
|
24
|
+
| Portable settings (non-sensitive) | ✅ Settings snapshot (secrets stripped) |
|
|
25
|
+
| Touched files & tool events | ✅ File touch history & event log |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Supported Tools
|
|
30
|
+
|
|
31
|
+
| Tool | Import | Export | MCP | Skills |
|
|
32
|
+
|------|:------:|:------:|:---:|:------:|
|
|
33
|
+
| **Claude Code** | ✅ | ✅ | ✅ | ✅ |
|
|
34
|
+
| **Gemini CLI** | ✅ | ✅ | ✅ | ✅ |
|
|
35
|
+
| **OpenCode** | ✅ | ✅ | ✅ | ✅ |
|
|
36
|
+
| **Codex CLI** | ✅ | ✅ | ✅ | ✅ |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Install via npm (recommended)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g @work-bridge/work-bridge
|
|
46
|
+
work-bridge
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Install via Go
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
go install github.com/jaeyoung0509/work-bridge/cmd/work-bridge@latest
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Download binary
|
|
56
|
+
|
|
57
|
+
Grab the latest release binary from [GitHub Releases](https://github.com/jaeyoung0509/work-bridge/releases).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### TUI (Interactive Mode)
|
|
64
|
+
|
|
65
|
+
Just run `work-bridge` in your terminal for the full interactive experience:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
work-bridge
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The TUI provides five panels:
|
|
72
|
+
|
|
73
|
+
| Panel | What it does |
|
|
74
|
+
|-------|-------------|
|
|
75
|
+
| **Sessions** | Browse, inspect, import, doctor-check, and export sessions |
|
|
76
|
+
| **Projects** | Index project roots from configured workspace roots |
|
|
77
|
+
| **Skills** | Compare project/user/global skill coverage and sync across scopes |
|
|
78
|
+
| **MCP** | Inspect config locations, merge effective scope, and run runtime validation |
|
|
79
|
+
| **Logs** | View recent workspace actions and errors |
|
|
80
|
+
|
|
81
|
+
**Mouse support:** pane focus · list selection · preview tab switching · scroll
|
|
82
|
+
|
|
83
|
+
### Migration Workflow
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
You were using Claude Code → now switching to Gemini CLI
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 1. Inspect what Claude Code has
|
|
91
|
+
work-bridge inspect claude --limit 5
|
|
92
|
+
|
|
93
|
+
# 2. Import the latest session into a portable bundle
|
|
94
|
+
work-bridge import --from claude --session latest --out ./bundle.json
|
|
95
|
+
|
|
96
|
+
# 3. Check compatibility with the target tool
|
|
97
|
+
work-bridge doctor --from claude --session latest --target gemini
|
|
98
|
+
|
|
99
|
+
# 4. Export target-native artifacts
|
|
100
|
+
work-bridge export --bundle ./bundle.json --target gemini --out ./out/
|
|
101
|
+
|
|
102
|
+
# 5. Start Gemini CLI — it'll pick up GEMINI.work-bridge.md automatically
|
|
103
|
+
cd ./out && gemini
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The exported `./out/` directory contains:
|
|
107
|
+
|
|
108
|
+
- `GEMINI.work-bridge.md` — context supplement injected into Gemini CLI
|
|
109
|
+
- `SETTINGS_PATCH.json` — portable settings to apply
|
|
110
|
+
- `STARTER_PROMPT.md` — copy-paste prompt to resume your task
|
|
111
|
+
- `manifest.json` — export manifest with portability warnings
|
|
112
|
+
|
|
113
|
+
### Pack / Unpack (Portable Archives)
|
|
114
|
+
|
|
115
|
+
Share your session state across machines or teammates:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Pack a session into a portable .spkg archive
|
|
119
|
+
work-bridge pack --from claude --session latest --out ./my-session.spkg
|
|
120
|
+
|
|
121
|
+
# Unpack on another machine and target a different tool
|
|
122
|
+
work-bridge unpack --file ./my-session.spkg --target codex --out ./out/
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Detect
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Auto-detect all installed tools and project artifacts
|
|
129
|
+
work-bridge detect
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### CLI Reference
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
work-bridge [flags]
|
|
136
|
+
work-bridge detect
|
|
137
|
+
work-bridge inspect <tool> [--limit N]
|
|
138
|
+
work-bridge import --from <tool> [--session <id|latest>] [--out <path>]
|
|
139
|
+
work-bridge doctor --from <tool> [--session <id|latest>] --target <tool>
|
|
140
|
+
work-bridge export --bundle <path> --target <tool> [--out <dir>]
|
|
141
|
+
work-bridge pack --from <tool> [--session <id|latest>] --out <path>
|
|
142
|
+
work-bridge unpack --file <path> --target <tool> [--out <dir>]
|
|
143
|
+
work-bridge version
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Supported tools:** `claude` · `gemini` · `codex` · `opencode`
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## How It Works
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
154
|
+
│ work-bridge │
|
|
155
|
+
│ │
|
|
156
|
+
│ detect/ ──► inspect/ ──► importer/ ──► domain.SessionBundle │
|
|
157
|
+
│ │ │
|
|
158
|
+
│ doctor/ │
|
|
159
|
+
│ (compatibility check) │
|
|
160
|
+
│ │ │
|
|
161
|
+
│ exporter/ │
|
|
162
|
+
│ (target-native artifacts) │
|
|
163
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
164
|
+
|
|
165
|
+
SessionBundle fields:
|
|
166
|
+
source_tool · source_session_id · project_root
|
|
167
|
+
task_title · current_goal · summary
|
|
168
|
+
instruction_artifacts (AGENTS.md, CLAUDE.md, GEMINI.md …)
|
|
169
|
+
settings_snapshot (sensitive keys redacted automatically)
|
|
170
|
+
tool_events · touched_files
|
|
171
|
+
decisions · failures · resume_hints
|
|
172
|
+
token_stats · provenance · redactions
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Security & Redaction
|
|
176
|
+
|
|
177
|
+
`work-bridge` automatically strips sensitive values during import:
|
|
178
|
+
|
|
179
|
+
- Key-based: `secret`, `token`, `password`, `auth`, `oauth`, `credential`, `api_key`, `apikey`
|
|
180
|
+
- Value-based heuristics: `sk-*`, `ghp_*`, `github_pat_*`, `AIza*` prefixes, and long random-looking strings
|
|
181
|
+
- Redacted fields are listed in `bundle.redactions` for transparency — nothing is silently dropped
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## MCP Validation
|
|
186
|
+
|
|
187
|
+
The **MCP** panel (and `inspect` output) runs a real runtime handshake for supported transports:
|
|
188
|
+
|
|
189
|
+
1. Spawns the server process (stdio) or connects (HTTP / SSE)
|
|
190
|
+
2. Sends `initialize` → waits for capability response
|
|
191
|
+
3. Sends `notifications/initialized`
|
|
192
|
+
4. Counts advertised `resources`, `resourceTemplates`, `tools`, and `prompts`
|
|
193
|
+
|
|
194
|
+
This catches config problems that a static lint pass would miss.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Configuration
|
|
199
|
+
|
|
200
|
+
Configuration is resolved in this priority order:
|
|
201
|
+
|
|
202
|
+
1. CLI flags
|
|
203
|
+
2. Environment variables (`WORK_BRIDGE_` prefix)
|
|
204
|
+
3. `--config <file>`
|
|
205
|
+
4. Auto-discovered config in CWD → then home directory
|
|
206
|
+
5. Built-in defaults
|
|
207
|
+
|
|
208
|
+
### Supported Config Files
|
|
209
|
+
|
|
210
|
+
- `.work-bridge.yaml` / `.work-bridge.yml`
|
|
211
|
+
- `.work-bridge.toml`
|
|
212
|
+
- `.work-bridge.json`
|
|
213
|
+
|
|
214
|
+
### Key Config Fields
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
# .work-bridge.yaml
|
|
218
|
+
workspace_roots:
|
|
219
|
+
- ~/Projects
|
|
220
|
+
- ~/work
|
|
221
|
+
|
|
222
|
+
paths:
|
|
223
|
+
codex: ~/.local/share/codex
|
|
224
|
+
gemini: ~/.config/gemini
|
|
225
|
+
claude: ~/.claude
|
|
226
|
+
opencode: ~/.config/opencode
|
|
227
|
+
|
|
228
|
+
output:
|
|
229
|
+
export_dir: ./out/work-bridge
|
|
230
|
+
package_path: ./session.spkg
|
|
231
|
+
unpack_dir: ./out/unpacked
|
|
232
|
+
|
|
233
|
+
redaction:
|
|
234
|
+
detect_sensitive_values: true
|
|
235
|
+
additional_sensitive_keys:
|
|
236
|
+
- my_internal_token
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### JSON Example
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"format": "json",
|
|
244
|
+
"workspace_roots": ["~/Projects", "~/work"],
|
|
245
|
+
"paths": {
|
|
246
|
+
"codex": "/Users/me/.local/share/codex"
|
|
247
|
+
},
|
|
248
|
+
"output": {
|
|
249
|
+
"export_dir": "./out/work-bridge"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Environment Variables
|
|
255
|
+
|
|
256
|
+
| Variable | Config Key |
|
|
257
|
+
|----------|-----------|
|
|
258
|
+
| `WORK_BRIDGE_FORMAT` | `format` |
|
|
259
|
+
| `WORK_BRIDGE_WORKSPACE_ROOTS` | `workspace_roots` |
|
|
260
|
+
| `WORK_BRIDGE_PATHS_CODEX` | `paths.codex` |
|
|
261
|
+
| `WORK_BRIDGE_PATHS_GEMINI` | `paths.gemini` |
|
|
262
|
+
| `WORK_BRIDGE_PATHS_CLAUDE` | `paths.claude` |
|
|
263
|
+
| `WORK_BRIDGE_PATHS_OPENCODE` | `paths.opencode` |
|
|
264
|
+
| `WORK_BRIDGE_OUTPUT_EXPORT_DIR` | `output.export_dir` |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Repository Layout
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
cmd/work-bridge/ CLI entrypoint (main.go)
|
|
272
|
+
internal/
|
|
273
|
+
cli/ Cobra/Viper root command, TUI backend wiring
|
|
274
|
+
app.go App struct, Run(), Config
|
|
275
|
+
root_tui.go TUI launch logic and Backend wiring
|
|
276
|
+
legacy_commands.go detect/inspect/import/doctor/export commands
|
|
277
|
+
package_commands.go pack/unpack commands
|
|
278
|
+
root_tui_backend.go All TUI backend action implementations
|
|
279
|
+
tui/ Bubble Tea v2 interactive workspace
|
|
280
|
+
domain/ Portable bundle types (SessionBundle, Tool, …)
|
|
281
|
+
importer/ Tool-specific session importers
|
|
282
|
+
claude.go / codex.go / gemini.go / opencode.go
|
|
283
|
+
normalizer.go Signal extraction & normalization
|
|
284
|
+
signals.go Decision/failure/hint signal detection
|
|
285
|
+
exporter/ Target-native artifact generation
|
|
286
|
+
detect/ Tool installation & project artifact detection
|
|
287
|
+
inspect/ Session listing for each tool
|
|
288
|
+
doctor/ Cross-tool compatibility analysis
|
|
289
|
+
catalog/ Skills catalog (project/user/global scopes)
|
|
290
|
+
capability/ MCP capability registry
|
|
291
|
+
packagex/ .spkg pack/unpack (zip-based)
|
|
292
|
+
platform/ FS, clock, archive, env, redact utilities
|
|
293
|
+
testdata/ Fixtures and golden outputs
|
|
294
|
+
scripts/
|
|
295
|
+
install.cjs npm postinstall binary downloader
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Building from Source
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
git clone https://github.com/jaeyoung0509/work-bridge.git
|
|
304
|
+
cd work-bridge
|
|
305
|
+
|
|
306
|
+
# Build
|
|
307
|
+
make build
|
|
308
|
+
./bin/work-bridge
|
|
309
|
+
|
|
310
|
+
# Test
|
|
311
|
+
make test
|
|
312
|
+
|
|
313
|
+
# Lint
|
|
314
|
+
make lint
|
|
315
|
+
|
|
316
|
+
# Format
|
|
317
|
+
make fmt
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Requirements:** Go 1.21+
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Publishing a Release
|
|
325
|
+
|
|
326
|
+
Releases use [GoReleaser](https://goreleaser.com/). A GitHub Actions workflow triggers on version tags:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
git tag v0.1.0
|
|
330
|
+
git push origin v0.1.0
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
GoReleaser builds cross-platform binaries (`darwin/amd64`, `darwin/arm64`, `linux/amd64`, `linux/arm64`, `windows/amd64`, `windows/arm64`) and uploads them as `.tar.gz` / `.zip` archives to the GitHub Release.
|
|
334
|
+
|
|
335
|
+
The npm package wrapper then downloads the correct binary at install time via `scripts/install.cjs`.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Contributing
|
|
340
|
+
|
|
341
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
342
|
+
|
|
343
|
+
**Good first areas:**
|
|
344
|
+
- Add a new tool importer under `internal/importer/`
|
|
345
|
+
- Extend `internal/exporter/` with a new target format
|
|
346
|
+
- Improve MCP runtime validation for new transport types
|
|
347
|
+
- Add fixture-backed tests for edge cases
|
|
348
|
+
|
|
349
|
+
Principles:
|
|
350
|
+
- Local-first, no network calls during normal operation
|
|
351
|
+
- Deterministic output for fixture-backed tests
|
|
352
|
+
- Sensitive data must never leave the machine unredacted
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT © [jaeyoung0509](https://github.com/jaeyoung0509)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('node:child_process');
|
|
4
|
+
const { existsSync } = require('node:fs');
|
|
5
|
+
const { join } = require('node:path');
|
|
6
|
+
|
|
7
|
+
const binary = join(__dirname, 'work-bridge');
|
|
8
|
+
|
|
9
|
+
if (!existsSync(binary)) {
|
|
10
|
+
console.error('work-bridge binary is not installed yet. Re-run npm install or check the release assets.');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
|
|
15
|
+
process.exit(result.status ?? 1);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@work-bridge/work-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Portable session migration CLI for Claude Code, Gemini CLI, OpenCode, and Codex CLI.",
|
|
5
|
+
"homepage": "https://github.com/jaeyoung0509/work-bridge",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jaeyoung0509/work-bridge.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/jaeyoung0509/work-bridge/issues"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude",
|
|
15
|
+
"gemini",
|
|
16
|
+
"codex",
|
|
17
|
+
"opencode",
|
|
18
|
+
"mcp",
|
|
19
|
+
"session",
|
|
20
|
+
"migration",
|
|
21
|
+
"ai",
|
|
22
|
+
"coding-agent",
|
|
23
|
+
"tui"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bin": {
|
|
27
|
+
"work-bridge": "bin/work-bridge.js"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node scripts/install.cjs"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"bin/work-bridge.js",
|
|
37
|
+
"scripts/install.cjs"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const https = require('node:https');
|
|
5
|
+
const { spawnSync } = require('node:child_process');
|
|
6
|
+
|
|
7
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
8
|
+
const vendorDir = path.join(pkgRoot, 'bin');
|
|
9
|
+
const binaryName = process.platform === 'win32' ? 'work-bridge.exe' : 'work-bridge';
|
|
10
|
+
|
|
11
|
+
const repo =
|
|
12
|
+
process.env.WORK_BRIDGE_GITHUB_REPO ||
|
|
13
|
+
process.env.npm_package_repository_url ||
|
|
14
|
+
process.env.npm_package_repository ||
|
|
15
|
+
'';
|
|
16
|
+
const releaseTag = process.env.WORK_BRIDGE_VERSION || process.env.npm_package_version || 'latest';
|
|
17
|
+
|
|
18
|
+
if (!repo) {
|
|
19
|
+
console.warn('[work-bridge] WORK_BRIDGE_GITHUB_REPO is not set; skipping binary download.');
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const [owner, name] = repo.replace(/^git\+https:\/\/github.com\//, '').replace(/^https:\/\/github.com\//, '').replace(/\.git$/, '').split('/').slice(-2);
|
|
24
|
+
if (!owner || !name) {
|
|
25
|
+
console.warn('[work-bridge] Could not parse GitHub repository from WORK_BRIDGE_GITHUB_REPO; skipping binary download.');
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const platform = platformId(process.platform);
|
|
30
|
+
const arch = archId(process.arch);
|
|
31
|
+
if (!platform || !arch) {
|
|
32
|
+
console.warn(`[work-bridge] Unsupported platform ${process.platform}/${process.arch}; skipping binary download.`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const assetName = `work-bridge_${platform}_${arch}.tar.gz`;
|
|
37
|
+
const url = `https://github.com/${owner}/${name}/releases/download/${releaseTag}/${assetName}`;
|
|
38
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'work-bridge-'));
|
|
39
|
+
const archivePath = path.join(tmpDir, assetName);
|
|
40
|
+
|
|
41
|
+
download(url, archivePath, (err) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
console.warn(`[work-bridge] Download failed: ${err.message}`);
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fs.mkdirSync(vendorDir, { recursive: true });
|
|
48
|
+
const extract = spawnSync('tar', ['-xzf', archivePath, '-C', vendorDir], { stdio: 'inherit' });
|
|
49
|
+
if (extract.status !== 0) {
|
|
50
|
+
console.warn('[work-bridge] Extraction failed; the wrapper will remain inert until a successful reinstall.');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const extractedBinary = path.join(vendorDir, binaryName);
|
|
55
|
+
if (process.platform !== 'win32' && fs.existsSync(extractedBinary)) {
|
|
56
|
+
fs.chmodSync(extractedBinary, 0o755);
|
|
57
|
+
}
|
|
58
|
+
process.exit(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function download(url, dest, done) {
|
|
62
|
+
const file = fs.createWriteStream(dest);
|
|
63
|
+
const request = https.get(url, (response) => {
|
|
64
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
65
|
+
file.close();
|
|
66
|
+
return download(response.headers.location, dest, done);
|
|
67
|
+
}
|
|
68
|
+
if (response.statusCode !== 200) {
|
|
69
|
+
file.close();
|
|
70
|
+
return done(new Error(`unexpected status ${response.statusCode}`));
|
|
71
|
+
}
|
|
72
|
+
response.pipe(file);
|
|
73
|
+
file.on('finish', () => {
|
|
74
|
+
file.close(done);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
request.on('error', (err) => {
|
|
79
|
+
file.close();
|
|
80
|
+
done(err);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function platformId(platform) {
|
|
85
|
+
switch (platform) {
|
|
86
|
+
case 'darwin':
|
|
87
|
+
case 'linux':
|
|
88
|
+
return platform;
|
|
89
|
+
case 'win32':
|
|
90
|
+
return 'windows';
|
|
91
|
+
default:
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function archId(arch) {
|
|
97
|
+
switch (arch) {
|
|
98
|
+
case 'x64':
|
|
99
|
+
return 'amd64';
|
|
100
|
+
case 'arm64':
|
|
101
|
+
return 'arm64';
|
|
102
|
+
default:
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
}
|