@web-auto/camo 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/LICENSE +21 -0
- package/README.md +243 -0
- package/bin/camo.mjs +22 -0
- package/package.json +39 -0
- package/scripts/build.mjs +19 -0
- package/scripts/bump-version.mjs +38 -0
- package/scripts/install.mjs +76 -0
- package/scripts/release.sh +54 -0
- package/src/cli.mjs +199 -0
- package/src/commands/browser.mjs +462 -0
- package/src/commands/cookies.mjs +69 -0
- package/src/commands/create.mjs +98 -0
- package/src/commands/init.mjs +68 -0
- package/src/commands/lifecycle.mjs +256 -0
- package/src/commands/mouse.mjs +49 -0
- package/src/commands/profile.mjs +46 -0
- package/src/commands/system.mjs +14 -0
- package/src/commands/window.mjs +31 -0
- package/src/lifecycle/cleanup.mjs +83 -0
- package/src/lifecycle/lock.mjs +122 -0
- package/src/lifecycle/session-registry.mjs +163 -0
- package/src/utils/args.mjs +25 -0
- package/src/utils/browser-service.mjs +194 -0
- package/src/utils/config.mjs +90 -0
- package/src/utils/fingerprint.mjs +181 -0
- package/src/utils/help.mjs +128 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jason Zhang
|
|
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,243 @@
|
|
|
1
|
+
# Camo CLI
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Jasonzhangf/camo/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@web-auto/camo)
|
|
5
|
+
|
|
6
|
+
A cross-platform command-line interface for Camoufox browser automation.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### npm (Recommended)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g @web-auto/camo
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### From Source
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/Jasonzhangf/camo.git
|
|
20
|
+
cd camo
|
|
21
|
+
npm run build:global
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Initialize environment
|
|
28
|
+
camo init
|
|
29
|
+
|
|
30
|
+
# Create a profile
|
|
31
|
+
camo profile create myprofile
|
|
32
|
+
|
|
33
|
+
# Set as default
|
|
34
|
+
camo profile default myprofile
|
|
35
|
+
|
|
36
|
+
# Start browser
|
|
37
|
+
camo start --url https://example.com
|
|
38
|
+
|
|
39
|
+
# Navigate
|
|
40
|
+
camo goto https://www.xiaohongshu.com
|
|
41
|
+
|
|
42
|
+
# Interact
|
|
43
|
+
camo click "#search-input"
|
|
44
|
+
camo type "#search-input" "hello world"
|
|
45
|
+
camo scroll --down --amount 500
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Commands
|
|
49
|
+
|
|
50
|
+
### Profile Management
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
camo profiles # List profiles with default profile
|
|
54
|
+
camo profile create <profileId> # Create a profile
|
|
55
|
+
camo profile delete <profileId> # Delete a profile
|
|
56
|
+
camo profile default [profileId] # Get or set default profile
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Initialization
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
camo init # Ensure camoufox + browser-service
|
|
63
|
+
camo init geoip # Download GeoIP database
|
|
64
|
+
camo init list # List available OS and regions
|
|
65
|
+
camo create fingerprint --os <os> --region <region>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Browser Control
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
camo start [profileId] [--url <url>] [--headless]
|
|
72
|
+
camo stop [profileId]
|
|
73
|
+
camo status [profileId]
|
|
74
|
+
camo shutdown # Shutdown browser-service (all sessions)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Lifecycle & Cleanup
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
camo sessions # List active browser sessions
|
|
81
|
+
camo cleanup [profileId] # Cleanup session (release lock + stop)
|
|
82
|
+
camo cleanup all # Cleanup all active sessions
|
|
83
|
+
camo cleanup locks # Cleanup stale lock files
|
|
84
|
+
camo force-stop [profileId] # Force stop session (for stuck sessions)
|
|
85
|
+
camo lock list # List active session locks
|
|
86
|
+
camo recover [profileId] # Recover orphaned session
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Navigation
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
camo goto [profileId] <url> # Navigate to URL
|
|
93
|
+
camo back [profileId] # Navigate back
|
|
94
|
+
camo screenshot [profileId] [--output <file>] [--full]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Interaction
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
camo scroll [profileId] [--down|--up|--left|--right] [--amount <px>]
|
|
101
|
+
camo click [profileId] <selector> # Click element by CSS selector
|
|
102
|
+
camo type [profileId] <selector> <text> # Type text into element
|
|
103
|
+
camo highlight [profileId] <selector> # Highlight element (red border, 2s)
|
|
104
|
+
camo clear-highlight [profileId] # Clear all highlights
|
|
105
|
+
camo viewport [profileId] --width <w> --height <h>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Pages
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
camo new-page [profileId] [--url <url>]
|
|
112
|
+
camo close-page [profileId] [index]
|
|
113
|
+
camo switch-page [profileId] <index>
|
|
114
|
+
camo list-pages [profileId]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Cookies
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
camo cookies get [profileId] Get all cookies for profile
|
|
121
|
+
camo cookies save [profileId] --path <file> Save cookies to file
|
|
122
|
+
camo cookies load [profileId] --path <file> Load cookies from file
|
|
123
|
+
camo cookies auto start [profileId] [--interval <ms>] Start auto-saving cookies
|
|
124
|
+
camo cookies auto stop [profileId] Stop auto-saving
|
|
125
|
+
camo cookies auto status [profileId] Check auto-save status
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Window Control
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
camo window move [profileId] --x <x> --y <y>
|
|
132
|
+
camo window resize [profileId] --width <w> --height <h>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Mouse Control
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
camo mouse move [profileId] --x <x> --y <y> [--steps <n>]
|
|
139
|
+
camo mouse click [profileId] --x <x> --y <y> [--button left|right|middle] [--clicks <n>] [--delay <ms>]
|
|
140
|
+
camo mouse wheel [profileId] [--deltax <px>] [--deltay <px>]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### System
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
camo system display # Show display metrics
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Fingerprint Options
|
|
150
|
+
|
|
151
|
+
### OS Options
|
|
152
|
+
|
|
153
|
+
- `mac` (default) - macOS (auto architecture)
|
|
154
|
+
- `mac-m1` - macOS with Apple Silicon
|
|
155
|
+
- `mac-intel` - macOS with Intel
|
|
156
|
+
- `windows` - Windows 11
|
|
157
|
+
- `windows-10` - Windows 10
|
|
158
|
+
- `linux` - Ubuntu 22.04
|
|
159
|
+
|
|
160
|
+
### Region Options
|
|
161
|
+
|
|
162
|
+
- `us` (default) - United States (New York)
|
|
163
|
+
- `us-west` - United States (Los Angeles)
|
|
164
|
+
- `uk` - United Kingdom (London)
|
|
165
|
+
- `de` - Germany (Berlin)
|
|
166
|
+
- `fr` - France (Paris)
|
|
167
|
+
- `jp` - Japan (Tokyo)
|
|
168
|
+
- `sg` - Singapore
|
|
169
|
+
- `au` - Australia (Sydney)
|
|
170
|
+
- `hk` - Hong Kong
|
|
171
|
+
- `tw` - Taiwan (Taipei)
|
|
172
|
+
- `br` - Brazil (Sao Paulo)
|
|
173
|
+
- `in` - India (Mumbai)
|
|
174
|
+
|
|
175
|
+
## Configuration
|
|
176
|
+
|
|
177
|
+
- Config file: `~/.webauto/camo-cli.json`
|
|
178
|
+
- Profiles directory: `~/.webauto/profiles/`
|
|
179
|
+
- Fingerprints directory: `~/.webauto/fingerprints/`
|
|
180
|
+
- Session registry: `~/.webauto/sessions/`
|
|
181
|
+
- Lock files: `~/.webauto/locks/`
|
|
182
|
+
- GeoIP database: `~/.webauto/geoip/GeoLite2-City.mmdb`
|
|
183
|
+
|
|
184
|
+
### Environment Variables
|
|
185
|
+
|
|
186
|
+
- `WEBAUTO_BROWSER_URL` - Browser service URL (default: `http://127.0.0.1:7704`)
|
|
187
|
+
- `WEBAUTO_REPO_ROOT` - WebAuto repository root (optional)
|
|
188
|
+
|
|
189
|
+
## Session Persistence
|
|
190
|
+
|
|
191
|
+
Camo CLI persists session information locally:
|
|
192
|
+
|
|
193
|
+
- Sessions are registered in `~/.webauto/sessions/`
|
|
194
|
+
- On restart, `camo sessions` shows both live and orphaned sessions
|
|
195
|
+
- Use `camo recover <profileId>` to reconnect or cleanup orphaned sessions
|
|
196
|
+
- Stale sessions (>7 days) are automatically cleaned up
|
|
197
|
+
|
|
198
|
+
## Requirements
|
|
199
|
+
|
|
200
|
+
- Node.js >= 20.0.0
|
|
201
|
+
- Python 3 with `camoufox` package
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Install dependencies
|
|
207
|
+
npm install
|
|
208
|
+
|
|
209
|
+
# Build
|
|
210
|
+
npm run build
|
|
211
|
+
|
|
212
|
+
# Test
|
|
213
|
+
npm test
|
|
214
|
+
|
|
215
|
+
# Global install (build + test + install)
|
|
216
|
+
npm run build:global
|
|
217
|
+
|
|
218
|
+
# Bump version
|
|
219
|
+
npm run version:bump
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Release
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# Create a release (bumps version, runs tests, creates tag)
|
|
226
|
+
./scripts/release.sh
|
|
227
|
+
|
|
228
|
+
# Or manually:
|
|
229
|
+
npm run version:bump
|
|
230
|
+
npm test
|
|
231
|
+
git add package.json
|
|
232
|
+
git commit -m "chore: release v$(node -p "require('./package.json').version")"
|
|
233
|
+
git tag "v$(node -p "require('./package.json').version")"
|
|
234
|
+
git push --follow-tags
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
GitHub Actions will automatically:
|
|
238
|
+
1. Run tests on push to main
|
|
239
|
+
2. Publish to npm when a release is created
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT
|
package/bin/camo.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const cliPath = path.join(__dirname, '..', 'src', 'cli.mjs');
|
|
8
|
+
|
|
9
|
+
// Delegate to the modular CLI
|
|
10
|
+
const child = spawn(process.execPath, [cliPath, ...process.argv.slice(2)], {
|
|
11
|
+
stdio: 'inherit',
|
|
12
|
+
env: process.env,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
child.on('error', (err) => {
|
|
16
|
+
console.error(`Failed to start camo: ${err.message}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
child.on('exit', (code) => {
|
|
21
|
+
process.exit(code || 0);
|
|
22
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@web-auto/camo",
|
|
3
|
+
"version": "0.1.0002",
|
|
4
|
+
"description": "Camoufox Browser CLI - Cross-platform browser automation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"camo": "./bin/camo.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"scripts/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node scripts/build.mjs",
|
|
18
|
+
"test": "node --test tests/*.test.mjs",
|
|
19
|
+
"version:bump": "node scripts/bump-version.mjs",
|
|
20
|
+
"install:global": "npm run build && npm install -g .",
|
|
21
|
+
"uninstall:global": "npm uninstall -g @web-auto/camo",
|
|
22
|
+
"build:global": "npm run build && npm test && npm install -g ."
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"browser",
|
|
26
|
+
"automation",
|
|
27
|
+
"camoufox",
|
|
28
|
+
"cli",
|
|
29
|
+
"playwright"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"camoufox": "^0.1.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { copyFileSync, chmodSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const root = path.resolve(__dirname, '..');
|
|
8
|
+
|
|
9
|
+
// Ensure bin directory exists
|
|
10
|
+
const binDir = path.join(root, 'bin');
|
|
11
|
+
try { mkdirSync(binDir, { recursive: true }); } catch {}
|
|
12
|
+
|
|
13
|
+
// Copy source files to dist (if needed)
|
|
14
|
+
// For now, we're running directly from src/
|
|
15
|
+
|
|
16
|
+
// Make bin/camo.mjs executable
|
|
17
|
+
const binFile = path.join(binDir, 'camo.mjs');
|
|
18
|
+
chmodSync(binFile, 0o755);
|
|
19
|
+
console.log('Build: bin/camo.mjs ready');
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Version bumper for camo CLI
|
|
4
|
+
* Increments patch version maintaining 4-digit format (0.1.0001 -> 0.1.0002)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
14
|
+
|
|
15
|
+
function bumpVersion() {
|
|
16
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
17
|
+
const currentVersion = pkg.version;
|
|
18
|
+
|
|
19
|
+
// Parse version: 0.1.0001 -> [0, 1, 1]
|
|
20
|
+
const parts = currentVersion.split('.');
|
|
21
|
+
const major = parseInt(parts[0], 10);
|
|
22
|
+
const minor = parseInt(parts[1], 10);
|
|
23
|
+
let patch = parseInt(parts[2], 10);
|
|
24
|
+
|
|
25
|
+
// Increment patch
|
|
26
|
+
patch += 1;
|
|
27
|
+
|
|
28
|
+
// Format with 4 digits
|
|
29
|
+
const newVersion = `${major}.${minor}.${patch.toString().padStart(4, '0')}`;
|
|
30
|
+
|
|
31
|
+
pkg.version = newVersion;
|
|
32
|
+
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
33
|
+
|
|
34
|
+
console.log(`Version bumped: ${currentVersion} -> ${newVersion}`);
|
|
35
|
+
return newVersion;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
bumpVersion();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* camo CLI Global Installer
|
|
4
|
+
* Usage: node scripts/install.mjs [--prefix /usr/local]
|
|
5
|
+
*/
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
|
|
11
|
+
const isWin = os.platform() === 'win32';
|
|
12
|
+
|
|
13
|
+
function detectPrefix() {
|
|
14
|
+
const prefixIdx = process.argv.indexOf('--prefix');
|
|
15
|
+
if (prefixIdx >= 0 && process.argv[prefixIdx + 1]) {
|
|
16
|
+
return process.argv[prefixIdx + 1];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const candidates = [
|
|
20
|
+
isWin ? path.join(os.homedir(), 'AppData', 'Local', 'camo') : null,
|
|
21
|
+
'/opt/homebrew',
|
|
22
|
+
'/usr/local',
|
|
23
|
+
'/usr',
|
|
24
|
+
path.join(os.homedir(), '.local'),
|
|
25
|
+
].filter(Boolean);
|
|
26
|
+
|
|
27
|
+
for (const p of candidates) {
|
|
28
|
+
try {
|
|
29
|
+
fs.accessSync(path.dirname(p), fs.constants.W_OK);
|
|
30
|
+
return p;
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return path.join(os.homedir(), '.local');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function install() {
|
|
38
|
+
const prefix = detectPrefix();
|
|
39
|
+
const binDir = path.join(prefix, 'bin');
|
|
40
|
+
const targetDir = path.join(prefix, 'share', 'camo');
|
|
41
|
+
|
|
42
|
+
console.log(`Installing camo CLI...`);
|
|
43
|
+
console.log(` Prefix: ${prefix}`);
|
|
44
|
+
console.log(` Target: ${targetDir}`);
|
|
45
|
+
console.log(` Bin: ${binDir}`);
|
|
46
|
+
|
|
47
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
48
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
49
|
+
|
|
50
|
+
const thisDir = path.dirname(new URL(import.meta.url).pathname);
|
|
51
|
+
const moduleDir = path.resolve(thisDir, '..');
|
|
52
|
+
const srcFile = path.join(moduleDir, 'bin', 'camoufox.mjs');
|
|
53
|
+
|
|
54
|
+
if (!fs.existsSync(srcFile)) {
|
|
55
|
+
console.error(`Source not found: ${srcFile}`);
|
|
56
|
+
console.error('Run: cp src/cli.mjs bin/camoufox.mjs');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const targetFile = path.join(targetDir, 'camoufox.mjs');
|
|
61
|
+
fs.copyFileSync(srcFile, targetFile);
|
|
62
|
+
fs.chmodSync(targetFile, 0o755);
|
|
63
|
+
|
|
64
|
+
const binPath = path.join(binDir, 'camo');
|
|
65
|
+
const wrapper = `#!/usr/bin/env sh
|
|
66
|
+
exec node "${targetFile}" "$@"`;
|
|
67
|
+
fs.writeFileSync(binPath, wrapper);
|
|
68
|
+
fs.chmodSync(binPath, 0o755);
|
|
69
|
+
|
|
70
|
+
console.log(`\n✅ camo CLI installed!`);
|
|
71
|
+
console.log(`\nAdd to PATH if needed:`);
|
|
72
|
+
console.log(` export PATH="${binDir}:$PATH"`);
|
|
73
|
+
console.log(`\nUsage: camo --help`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
install();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Release script for camo CLI
|
|
3
|
+
# Usage: ./scripts/release.sh [patch|minor|major]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
RELEASE_TYPE="${1:-patch}"
|
|
8
|
+
|
|
9
|
+
# Ensure we're on main branch
|
|
10
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
11
|
+
if [ "$CURRENT_BRANCH" != "main" ] && [ "$CURRENT_BRANCH" != "master" ]; then
|
|
12
|
+
echo "Error: Must be on main or master branch"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Ensure working tree is clean
|
|
17
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
18
|
+
echo "Error: Working tree is not clean. Commit or stash changes first."
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Pull latest
|
|
23
|
+
git pull --rebase
|
|
24
|
+
|
|
25
|
+
# Bump version
|
|
26
|
+
echo "Bumping version..."
|
|
27
|
+
npm run version:bump
|
|
28
|
+
|
|
29
|
+
# Get new version
|
|
30
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
31
|
+
echo "New version: $NEW_VERSION"
|
|
32
|
+
|
|
33
|
+
# Run tests
|
|
34
|
+
echo "Running tests..."
|
|
35
|
+
npm test
|
|
36
|
+
|
|
37
|
+
# Build
|
|
38
|
+
echo "Building..."
|
|
39
|
+
npm run build
|
|
40
|
+
|
|
41
|
+
# Commit version bump
|
|
42
|
+
git add package.json
|
|
43
|
+
git commit -m "chore: release v$NEW_VERSION"
|
|
44
|
+
|
|
45
|
+
# Create tag
|
|
46
|
+
git tag "v$NEW_VERSION"
|
|
47
|
+
|
|
48
|
+
# Push commit and tag
|
|
49
|
+
echo "Pushing to remote..."
|
|
50
|
+
git push origin HEAD
|
|
51
|
+
git push origin "v$NEW_VERSION"
|
|
52
|
+
|
|
53
|
+
echo "Release v$NEW_VERSION created successfully!"
|
|
54
|
+
echo "GitHub Actions will automatically publish to npm."
|