camoufox-cli 0.7.0__tar.gz → 0.7.2__tar.gz
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.
Potentially problematic release.
This version of camoufox-cli might be problematic. Click here for more details.
- camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1/.claude-plugin/marketplace.json +20 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/README.md +1 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/cli.ts +18 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/cli.test.ts +10 -1
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/skills/camoufox-cli/SKILL.md +1 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/skills/camoufox-cli/references/commands.md +1 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/cli.py +17 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_cli.py +17 -1
- camoufox_cli-0.7.2/.gitignore +30 -0
- camoufox_cli-0.7.2/LICENSE +21 -0
- camoufox_cli-0.7.2/Makefile +30 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2}/PKG-INFO +4 -3
- camoufox_cli-0.7.2/README.md +272 -0
- camoufox_cli-0.7.2/js/.gitignore +2 -0
- camoufox_cli-0.7.2/js/LICENSE +21 -0
- camoufox_cli-0.7.2/js/package-lock.json +2785 -0
- camoufox_cli-0.7.2/js/package.json +47 -0
- camoufox_cli-0.7.2/js/pnpm-lock.yaml +1599 -0
- camoufox_cli-0.7.2/js/scripts/postinstall.js +11 -0
- camoufox_cli-0.7.2/js/src/browser.ts +401 -0
- camoufox_cli-0.7.2/js/src/cli.ts +588 -0
- camoufox_cli-0.7.2/js/src/commands.ts +364 -0
- camoufox_cli-0.7.2/js/src/config.ts +140 -0
- camoufox_cli-0.7.2/js/src/daemon.ts +27 -0
- camoufox_cli-0.7.2/js/src/identity.ts +206 -0
- camoufox_cli-0.7.2/js/src/install.ts +179 -0
- camoufox_cli-0.7.2/js/src/protocol.ts +32 -0
- camoufox_cli-0.7.2/js/src/proxy.ts +40 -0
- camoufox_cli-0.7.2/js/src/refs.ts +65 -0
- camoufox_cli-0.7.2/js/src/server.ts +214 -0
- camoufox_cli-0.7.2/js/tests/browser.test.ts +122 -0
- camoufox_cli-0.7.2/js/tests/cli.test.ts +475 -0
- camoufox_cli-0.7.2/js/tests/commands.test.ts +189 -0
- camoufox_cli-0.7.2/js/tests/e2e.test.ts +294 -0
- camoufox_cli-0.7.2/js/tests/install.test.ts +205 -0
- camoufox_cli-0.7.2/js/tests/protocol.test.ts +98 -0
- camoufox_cli-0.7.2/js/tests/proxy.test.ts +55 -0
- camoufox_cli-0.7.2/js/tests/refs.test.ts +150 -0
- camoufox_cli-0.7.2/js/tests/server.test.ts +300 -0
- camoufox_cli-0.7.2/js/tsconfig.json +14 -0
- camoufox_cli-0.7.2/js/vitest.config.ts +7 -0
- camoufox_cli-0.7.2/pyproject.toml +51 -0
- camoufox_cli-0.7.2/skills/camoufox-cli/SKILL.md +386 -0
- camoufox_cli-0.7.2/skills/camoufox-cli/references/commands.md +150 -0
- camoufox_cli-0.7.2/skills/camoufox-cli/references/snapshot-refs.md +221 -0
- camoufox_cli-0.7.2/src/camoufox_cli/__init__.py +0 -0
- camoufox_cli-0.7.2/src/camoufox_cli/__main__.py +38 -0
- camoufox_cli-0.7.2/src/camoufox_cli/browser.py +316 -0
- camoufox_cli-0.7.2/src/camoufox_cli/cli.py +590 -0
- camoufox_cli-0.7.2/src/camoufox_cli/commands.py +406 -0
- camoufox_cli-0.7.2/src/camoufox_cli/config.py +130 -0
- camoufox_cli-0.7.2/src/camoufox_cli/identity.py +215 -0
- camoufox_cli-0.7.2/src/camoufox_cli/install.py +167 -0
- camoufox_cli-0.7.2/src/camoufox_cli/protocol.py +24 -0
- camoufox_cli-0.7.2/src/camoufox_cli/proxy.py +30 -0
- camoufox_cli-0.7.2/src/camoufox_cli/refs.py +83 -0
- camoufox_cli-0.7.2/src/camoufox_cli/server.py +189 -0
- camoufox_cli-0.7.2/tests/__init__.py +0 -0
- camoufox_cli-0.7.2/tests/fixture.html +22 -0
- camoufox_cli-0.7.2/tests/test_cli.py +418 -0
- camoufox_cli-0.7.2/tests/test_commands.py +556 -0
- camoufox_cli-0.7.2/tests/test_e2e.py +302 -0
- camoufox_cli-0.7.2/tests/test_install.py +201 -0
- camoufox_cli-0.7.2/tests/test_protocol.py +63 -0
- camoufox_cli-0.7.2/tests/test_proxy.py +37 -0
- camoufox_cli-0.7.2/tests/test_refs.py +112 -0
- camoufox_cli-0.7.2/tests/test_server.py +101 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/.gitignore +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/LICENSE +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/Makefile +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/.gitignore +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/LICENSE +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/package-lock.json +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/package.json +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/pnpm-lock.yaml +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/scripts/postinstall.js +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/browser.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/commands.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/config.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/daemon.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/identity.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/install.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/protocol.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/proxy.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/refs.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/server.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/browser.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/commands.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/e2e.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/install.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/protocol.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/proxy.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/refs.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tests/server.test.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/tsconfig.json +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/vitest.config.ts +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/pyproject.toml +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/skills/camoufox-cli/references/snapshot-refs.md +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/__init__.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/__main__.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/browser.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/commands.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/config.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/identity.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/install.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/protocol.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/proxy.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/refs.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/src/camoufox_cli/server.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/__init__.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/fixture.html +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_commands.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_e2e.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_install.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_protocol.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_proxy.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_refs.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/tests/test_server.py +0 -0
- {camoufox_cli-0.7.0 → camoufox_cli-0.7.2}/.claude-plugin/marketplace.json +0 -0
camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1/.claude-plugin/marketplace.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "camoufox-cli",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Benn Huang"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "Anti-detect browser automation CLI & Skills for AI agents, powered by Camoufox",
|
|
8
|
+
"version": "0.1.0"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "camoufox-cli",
|
|
13
|
+
"description": "Anti-detect browser automation skill with fingerprint spoofing for AI agents",
|
|
14
|
+
"source": "./",
|
|
15
|
+
"skills": [
|
|
16
|
+
"./skills/camoufox-cli"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
{camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/README.md
RENAMED
|
@@ -174,6 +174,7 @@ camoufox-cli cookies export file.json # Export cookies
|
|
|
174
174
|
--proxy <url> Proxy server (http:// or https://; auth: http://user:pass@host:port)
|
|
175
175
|
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
176
176
|
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
177
|
+
--version Print version and exit
|
|
177
178
|
```
|
|
178
179
|
|
|
179
180
|
## Config File
|
{camoufox_cli-0.7.0 → camoufox_cli-0.7.2/.claude/worktrees/cli-version-command-8852a1}/js/src/cli.ts
RENAMED
|
@@ -97,6 +97,16 @@ export function listSessions(): string[] {
|
|
|
97
97
|
return sessions.sort();
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
export function getVersion(): string {
|
|
101
|
+
// package.json ships in the npm package and sits one level above both src/ and dist/.
|
|
102
|
+
try {
|
|
103
|
+
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
104
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version ?? "unknown";
|
|
105
|
+
} catch {
|
|
106
|
+
return "unknown";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
// ---------------------------------------------------------------------------
|
|
101
111
|
// Arg parsing
|
|
102
112
|
// ---------------------------------------------------------------------------
|
|
@@ -379,6 +389,13 @@ function installSystemDeps(): void {
|
|
|
379
389
|
|
|
380
390
|
async function main() {
|
|
381
391
|
const argv = process.argv.slice(2);
|
|
392
|
+
|
|
393
|
+
// Short-circuit before parseArgs: --version has no command and needs no daemon.
|
|
394
|
+
if (argv.includes("--version")) {
|
|
395
|
+
console.log(getVersion());
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
382
399
|
const { flags, command } = parseArgs(argv);
|
|
383
400
|
|
|
384
401
|
// Resolve default persistent path
|
|
@@ -505,6 +522,7 @@ Flags:
|
|
|
505
522
|
--proxy <url> Proxy server (e.g. http://host:port or https://host:443)
|
|
506
523
|
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
507
524
|
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
525
|
+
--version Print version and exit
|
|
508
526
|
|
|
509
527
|
Config file:
|
|
510
528
|
~/.camoufox-cli/config.json sets defaults for the flags above (override the
|
|
@@ -2,7 +2,8 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { buildCommand, getSocketPath, getVersion, parseArgs } from "../src/cli.js";
|
|
6
7
|
import { loadDefaults } from "../src/config.js";
|
|
7
8
|
|
|
8
9
|
// buildCommand calls process.exit on error; mock it to throw instead
|
|
@@ -316,6 +317,14 @@ describe("getSocketPath", () => {
|
|
|
316
317
|
});
|
|
317
318
|
});
|
|
318
319
|
|
|
320
|
+
describe("getVersion", () => {
|
|
321
|
+
it("returns the version from package.json", () => {
|
|
322
|
+
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
323
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
324
|
+
expect(getVersion()).toBe(pkg.version);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
319
328
|
describe("config file", () => {
|
|
320
329
|
let dir: string;
|
|
321
330
|
let cfg: string;
|
|
@@ -354,6 +354,7 @@ camoufox-cli snapshot -i
|
|
|
354
354
|
--proxy <url> Proxy server (http:// or https://; auth: http://user:pass@host:port)
|
|
355
355
|
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
356
356
|
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
357
|
+
--version Print version and exit
|
|
357
358
|
```
|
|
358
359
|
|
|
359
360
|
**Config file (optional).** To avoid repeating flags, set defaults in `~/.camoufox-cli/config.json` (override path with `$CAMOUFOX_CLI_CONFIG`): a `default` block applies to all sessions, and an optional `sessions.<name>` block layers extra overrides on top whenever you run `--session <name>` (the name is just whatever you pass to `--session` — sessions are never pre-registered). Settable keys: `proxy`, `locale`, `geoip`, `persistent` (`true`/`false`/path), `headed`, `timeout`, `json`. Command-line flags always win over the file. Config is read only when a session's daemon first launches.
|
|
@@ -131,6 +131,7 @@ camoufox-cli --persistent [path] ... # Persistent identity — reuse the same fi
|
|
|
131
131
|
camoufox-cli --proxy <url> ... # Proxy server (e.g. http://host:port or http://user:pass@host:port)
|
|
132
132
|
camoufox-cli --no-geoip ... # Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
133
133
|
camoufox-cli --locale <tag> ... # Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
134
|
+
camoufox-cli --version # Print version and exit
|
|
134
135
|
```
|
|
135
136
|
|
|
136
137
|
### `--persistent` in detail
|
|
@@ -91,6 +91,16 @@ def ensure_daemon(session: str, headed: bool, timeout: int, persistent: str | No
|
|
|
91
91
|
spawn_daemon(session, headed, timeout, persistent, proxy, geoip, locale)
|
|
92
92
|
|
|
93
93
|
|
|
94
|
+
def get_version() -> str:
|
|
95
|
+
"""Version from installed package metadata; pyproject isn't shipped in the wheel."""
|
|
96
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
return version("camoufox-cli")
|
|
100
|
+
except PackageNotFoundError:
|
|
101
|
+
return "unknown"
|
|
102
|
+
|
|
103
|
+
|
|
94
104
|
def list_sessions() -> list[str]:
|
|
95
105
|
sessions = []
|
|
96
106
|
try:
|
|
@@ -401,6 +411,12 @@ def _install_system_deps() -> None:
|
|
|
401
411
|
|
|
402
412
|
def main():
|
|
403
413
|
args = sys.argv[1:]
|
|
414
|
+
|
|
415
|
+
# Short-circuit before parse_args: --version has no command and needs no daemon.
|
|
416
|
+
if "--version" in args:
|
|
417
|
+
print(get_version())
|
|
418
|
+
return
|
|
419
|
+
|
|
404
420
|
flags, command = parse_args(args)
|
|
405
421
|
|
|
406
422
|
# Resolve default persistent path
|
|
@@ -526,6 +542,7 @@ Flags:
|
|
|
526
542
|
--proxy <url> Proxy server (e.g. http://host:port or https://host:443)
|
|
527
543
|
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
528
544
|
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
545
|
+
--version Print version and exit
|
|
529
546
|
|
|
530
547
|
Config file:
|
|
531
548
|
~/.camoufox-cli/config.json sets defaults for the flags above (override the
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Tests for CLI argument parsing and command building."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import re
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
|
|
7
8
|
from camoufox_cli import config
|
|
8
|
-
from camoufox_cli.cli import build_command, parse_args, list_sessions, get_socket_path
|
|
9
|
+
from camoufox_cli.cli import build_command, parse_args, list_sessions, get_socket_path, get_version
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@pytest.fixture(autouse=True)
|
|
@@ -282,6 +283,21 @@ class TestGetSocketPath:
|
|
|
282
283
|
assert get_socket_path("my-session") == "/tmp/camoufox-cli-my-session.sock"
|
|
283
284
|
|
|
284
285
|
|
|
286
|
+
class TestGetVersion:
|
|
287
|
+
def test_returns_semver(self):
|
|
288
|
+
# The package is installed in the dev/test venv, so metadata resolves.
|
|
289
|
+
assert re.match(r"^\d+\.\d+\.\d+", get_version())
|
|
290
|
+
|
|
291
|
+
def test_unknown_when_not_installed(self, monkeypatch):
|
|
292
|
+
import importlib.metadata
|
|
293
|
+
|
|
294
|
+
def _raise(name):
|
|
295
|
+
raise importlib.metadata.PackageNotFoundError(name)
|
|
296
|
+
|
|
297
|
+
monkeypatch.setattr(importlib.metadata, "version", _raise)
|
|
298
|
+
assert get_version() == "unknown"
|
|
299
|
+
|
|
300
|
+
|
|
285
301
|
class TestConfigFile:
|
|
286
302
|
# --- config.load_defaults ---
|
|
287
303
|
def test_absent_file(self):
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.venv/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
uv.lock
|
|
11
|
+
|
|
12
|
+
# Node
|
|
13
|
+
node_modules/
|
|
14
|
+
js/README.md
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# Env
|
|
26
|
+
.env
|
|
27
|
+
|
|
28
|
+
# Runtime
|
|
29
|
+
*.sock
|
|
30
|
+
*.pid
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Benn Huang
|
|
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.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.PHONY: test test-unit test-integration test-e2e build publish publish-pip publish-npm clean
|
|
2
|
+
|
|
3
|
+
test:
|
|
4
|
+
python -m pytest tests/ -v
|
|
5
|
+
|
|
6
|
+
test-unit:
|
|
7
|
+
python -m pytest tests/ -v -m "not integration"
|
|
8
|
+
|
|
9
|
+
test-integration:
|
|
10
|
+
python -m pytest tests/ -v -m integration
|
|
11
|
+
|
|
12
|
+
test-e2e:
|
|
13
|
+
python -m pytest tests/test_e2e.py -v
|
|
14
|
+
cd js && npx vitest run tests/e2e.test.ts
|
|
15
|
+
|
|
16
|
+
build: clean
|
|
17
|
+
uv build
|
|
18
|
+
cd js && npm run build
|
|
19
|
+
|
|
20
|
+
publish: publish-pip publish-npm
|
|
21
|
+
|
|
22
|
+
publish-pip: build
|
|
23
|
+
uvx twine upload dist/*
|
|
24
|
+
|
|
25
|
+
publish-npm: build
|
|
26
|
+
cd js && npm publish --access public
|
|
27
|
+
|
|
28
|
+
clean:
|
|
29
|
+
rm -rf dist/
|
|
30
|
+
rm -rf js/dist/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camoufox-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Anti-detect browser CLI & Skills for AI agents, powered by Camoufox
|
|
5
5
|
Project-URL: Homepage, https://github.com/Bin-Huang/camoufox-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/Bin-Huang/camoufox-cli
|
|
@@ -15,7 +15,7 @@ Classifier: Intended Audience :: Developers
|
|
|
15
15
|
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
16
16
|
Classifier: Topic :: Software Development :: Testing
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
|
-
Requires-Dist: camoufox[geoip]
|
|
18
|
+
Requires-Dist: camoufox[geoip]==0.4.11
|
|
19
19
|
Requires-Dist: pillow
|
|
20
20
|
Requires-Dist: playwright==1.52.0
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
@@ -45,7 +45,7 @@ Tell your AI agent (e.g. OpenClaw):
|
|
|
45
45
|
Or install manually:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npm install -g camoufox-cli
|
|
48
|
+
npm install -g camoufox-cli # Requires Node.js 20.10+
|
|
49
49
|
camoufox-cli install # Download browser
|
|
50
50
|
```
|
|
51
51
|
|
|
@@ -196,6 +196,7 @@ camoufox-cli cookies export file.json # Export cookies
|
|
|
196
196
|
--proxy <url> Proxy server (http:// or https://; auth: http://user:pass@host:port)
|
|
197
197
|
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
198
198
|
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
199
|
+
--version Print version and exit
|
|
199
200
|
```
|
|
200
201
|
|
|
201
202
|
## Config File
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# camoufox-cli
|
|
2
|
+
|
|
3
|
+
Anti-detect browser CLI & Skills for AI agents, powered by [Camoufox](https://github.com/daijro/camoufox).
|
|
4
|
+
|
|
5
|
+
### Highlights
|
|
6
|
+
|
|
7
|
+
- C++-level fingerprint spoofing via Camoufox (canvas, WebGL, audio, screen metrics, fonts)
|
|
8
|
+
- Persistent identities: stable fingerprint, OS, and locale across launches with `--persistent`
|
|
9
|
+
- Accessibility-tree snapshots with `@ref` element targeting
|
|
10
|
+
- Session isolation with cookie import/export
|
|
11
|
+
- Shell commands, no code generation
|
|
12
|
+
|
|
13
|
+
### Works with
|
|
14
|
+
|
|
15
|
+
OpenClaw, Claude Code, Cursor, Codex, and any agent that can run shell commands.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Tell your AI agent (e.g. OpenClaw):
|
|
20
|
+
|
|
21
|
+
> Install this CLI and skills from https://github.com/Bin-Huang/camoufox-cli
|
|
22
|
+
|
|
23
|
+
Or install manually:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g camoufox-cli # Requires Node.js 20.10+
|
|
27
|
+
camoufox-cli install # Download browser
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or with pip:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pipx install camoufox-cli
|
|
34
|
+
camoufox-cli install # Download browser
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
On Linux, install system dependencies with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
camoufox-cli install --with-deps
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The browser is downloaded from GitHub releases. If the anonymous GitHub API
|
|
44
|
+
rate limit is exhausted (common on shared server/CI IPs), the installer
|
|
45
|
+
automatically falls back to scraping github.com release pages.
|
|
46
|
+
|
|
47
|
+
### Agent Skill
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Add skills for AI agents (Claude Code, Cursor, Codex, etc.)
|
|
51
|
+
npx skills add Bin-Huang/camoufox-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
camoufox-cli open https://example.com # Launch browser & navigate
|
|
58
|
+
camoufox-cli snapshot -i # Interactive elements only
|
|
59
|
+
# - link "More information..." [ref=e1]
|
|
60
|
+
camoufox-cli click @e1 # Click by ref
|
|
61
|
+
camoufox-cli close # Done
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
### Navigation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
camoufox-cli open <url> # Navigate to URL (starts daemon if needed)
|
|
70
|
+
camoufox-cli back # Go back
|
|
71
|
+
camoufox-cli forward # Go forward
|
|
72
|
+
camoufox-cli reload # Reload page
|
|
73
|
+
camoufox-cli url # Print current URL
|
|
74
|
+
camoufox-cli title # Print page title
|
|
75
|
+
camoufox-cli close # Close your tab (browser exits when the last tab closes)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Snapshot
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
camoufox-cli snapshot # Full accessibility tree
|
|
82
|
+
camoufox-cli snapshot -i # Interactive elements only
|
|
83
|
+
camoufox-cli snapshot -s "css-selector" # Scoped to CSS selector
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Output format:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
- heading "Example Domain" [level=1] [ref=e1]
|
|
90
|
+
- paragraph [ref=e2]
|
|
91
|
+
- link "More information..." [ref=e3]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Interaction
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
camoufox-cli click @e1 # Click element
|
|
98
|
+
camoufox-cli fill @e3 "search query" # Clear + type into input
|
|
99
|
+
camoufox-cli type @e3 "append text" # Type without clearing
|
|
100
|
+
camoufox-cli select @e5 "option text" # Select dropdown option
|
|
101
|
+
camoufox-cli check @e6 # Toggle checkbox
|
|
102
|
+
camoufox-cli hover @e2 # Hover over element
|
|
103
|
+
camoufox-cli press Enter # Press keyboard key
|
|
104
|
+
camoufox-cli press "Control+a" # Key combination
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Data Extraction
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
camoufox-cli text @e1 # Get text content of element
|
|
111
|
+
camoufox-cli text body # Get all page text
|
|
112
|
+
camoufox-cli eval "document.title" # Execute JavaScript
|
|
113
|
+
camoufox-cli screenshot # Screenshot (JSON with base64)
|
|
114
|
+
camoufox-cli screenshot page.png # Screenshot to file
|
|
115
|
+
camoufox-cli screenshot --full page.png # Full page screenshot
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Scroll & Wait
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
camoufox-cli scroll down # Scroll down 500px
|
|
122
|
+
camoufox-cli scroll up 1000 # Scroll up 1000px
|
|
123
|
+
camoufox-cli wait 2000 # Wait milliseconds
|
|
124
|
+
camoufox-cli wait @e1 # Wait for element to appear
|
|
125
|
+
camoufox-cli wait --url "*/dashboard" # Wait for URL pattern
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Tabs
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
camoufox-cli tabs # List open tabs (with owner names)
|
|
132
|
+
camoufox-cli switch 2 # Switch to tab by index
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Named tabs let concurrent agents share one browser — same fingerprint, same cookies/login state — while each keeps its own page, element refs, and history. Tabs share one browser process (commands from different tabs may queue behind a slow one); separate sessions run as independent processes in parallel:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
camoufox-cli --tab inbox-scan-x4q open https://app.example.com/a
|
|
139
|
+
camoufox-cli --tab report-pull-9kf open https://app.example.com/b
|
|
140
|
+
camoufox-cli --tab inbox-scan-x4q snapshot -i # refs are per tab
|
|
141
|
+
camoufox-cli --tab inbox-scan-x4q close # frees only this tab; browser exits when the last tab closes
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Tab names must be unique per agent (nothing enforces this): use a task slug plus a short shell-generated random suffix (e.g. `price-scan-$(openssl rand -hex 2)`), chosen once and reused.
|
|
145
|
+
|
|
146
|
+
### Sessions
|
|
147
|
+
|
|
148
|
+
Each named session is a separate browser process with its own randomly-generated fingerprint and its own cookies. Use sessions for isolated identities, named tabs (above) for cheap parallelism under one identity. For real multi-account isolation, combine sessions with a per-session proxy (config file) and `--persistent` — see Persistent Identity below:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
camoufox-cli sessions # List active sessions
|
|
152
|
+
camoufox-cli --session work open <url> # Use named session
|
|
153
|
+
camoufox-cli close --all # Force-close all sessions
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Cookies
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
camoufox-cli cookies # Dump cookies as JSON
|
|
160
|
+
camoufox-cli cookies import file.json # Import cookies
|
|
161
|
+
camoufox-cli cookies export file.json # Export cookies
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Flags
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
--session <name> Named session (default: "default")
|
|
168
|
+
--tab <name> Named tab within the session's shared browser: same fingerprint and
|
|
169
|
+
cookies/login, independent page/refs/history (default: "default")
|
|
170
|
+
--headed Show browser window (default: headless)
|
|
171
|
+
--timeout <seconds> Daemon idle timeout (default: 1800)
|
|
172
|
+
--json Output as JSON
|
|
173
|
+
--persistent [path] Persistent identity (see below); default path: ~/.camoufox-cli/profiles/<session>
|
|
174
|
+
--proxy <url> Proxy server (http:// or https://; auth: http://user:pass@host:port)
|
|
175
|
+
--no-geoip Disable automatic GeoIP spoofing (auto-enabled with --proxy)
|
|
176
|
+
--locale <tag> Force browser locale (e.g. "en-US" or "en-US,zh-CN")
|
|
177
|
+
--version Print version and exit
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Config File
|
|
181
|
+
|
|
182
|
+
Tired of repeating the same flags on every call? Put defaults in `~/.camoufox-cli/config.json` (override the path with `$CAMOUFOX_CLI_CONFIG`):
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"default": {
|
|
187
|
+
"persistent": true,
|
|
188
|
+
"timeout": 3600,
|
|
189
|
+
"proxy": "http://user:pass@host:8080"
|
|
190
|
+
},
|
|
191
|
+
"sessions": {
|
|
192
|
+
"<your-session-name>": {
|
|
193
|
+
"proxy": "socks5://127.0.0.1:1080",
|
|
194
|
+
"locale": "zh-CN"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- **`default`** applies to every session.
|
|
201
|
+
- **`sessions` is optional.** You never pre-register sessions — the name is just whatever you pass to `--session`. A `sessions.<name>` block only kicks in when you happen to run `--session <name>`, layering its keys on top of `default`; any other session (or no `--session` at all) just uses `default`. Above, `camoufox-cli --session <your-session-name> open <url>` swaps in that block's proxy + locale, while every other session keeps the `default` proxy.
|
|
202
|
+
- **Command-line flags always win** over the file, which in turn wins over built-in defaults.
|
|
203
|
+
- Settable keys: `proxy`, `locale`, `geoip`, `persistent` (`true` / `false` / path string), `headed`, `timeout`, `json`. `session` is command-line only (it selects which block to apply), and per-command options like `--full` or `-i` are never read from config.
|
|
204
|
+
- A broken config never blocks a command — it's ignored with a warning on stderr.
|
|
205
|
+
- Config is read when a session's daemon **first launches**. If one is already running for that session, run `camoufox-cli --session <name> close` first for changes to take effect.
|
|
206
|
+
|
|
207
|
+
## Persistent Identity
|
|
208
|
+
|
|
209
|
+
Without `--persistent`, every launch gets a fresh random fingerprint. With `--persistent`, a `camoufox-cli.json` file is written next to the browser's user data. Fingerprint, OS, and canvas/font seeds are generated on the first launch and frozen afterwards; locale and proxy-derived timezone/geolocation are also saved but track whatever you pass on the command line. Every subsequent launch with the same path reloads the identity, so sites see the same device on every visit.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# First launch: generates + stores identity
|
|
213
|
+
camoufox-cli --persistent ~/.camoufox-cli/profiles/alice open https://example.com
|
|
214
|
+
|
|
215
|
+
# Every subsequent launch: same fingerprint, same cookies, same device
|
|
216
|
+
camoufox-cli --persistent ~/.camoufox-cli/profiles/alice open https://example.com
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Frozen** — generated on the first launch, permanent until the directory is deleted:
|
|
220
|
+
|
|
221
|
+
- Fingerprint (UA, canvas, WebGL, fonts, navigator properties)
|
|
222
|
+
- OS (defaults to host OS)
|
|
223
|
+
- Canvas/font noise seeds
|
|
224
|
+
|
|
225
|
+
**Persisted** — stored in the file, refreshed whenever you pass the flag:
|
|
226
|
+
|
|
227
|
+
- `--locale`: sets the stored locale; pass it again to change it, omit it to keep the stored value.
|
|
228
|
+
- Timezone / geolocation: re-derived from the current proxy's IP whenever `--proxy` is active (and `--no-geoip` isn't set), then written back. Without a proxy, the last stored values stay in effect.
|
|
229
|
+
|
|
230
|
+
**Per-launch** — never stored; pass on every launch for it to apply:
|
|
231
|
+
|
|
232
|
+
- `--proxy`: proxies rotate or expire; re-specify each launch to avoid silently running through a stale one.
|
|
233
|
+
- `--no-geoip`: follows the current `--proxy`.
|
|
234
|
+
|
|
235
|
+
**Multiple identities in parallel.** Each persistent path is a distinct identity. Combine with `--session` for concurrent daemons:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
camoufox-cli --session a --persistent ~/.camoufox-cli/profiles/alice open https://...
|
|
239
|
+
camoufox-cli --session b --persistent ~/.camoufox-cli/profiles/bob open https://...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Resetting an identity.** Just delete the directory (`rm -rf ~/.camoufox-cli/profiles/alice`). The next launch generates a fresh one.
|
|
243
|
+
|
|
244
|
+
## Architecture
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
CLI (camoufox-cli) ──Unix socket──▶ Daemon (Python) ──Playwright──▶ Camoufox (Firefox)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
The CLI sends JSON commands to a long-running daemon process via Unix socket. The daemon manages the Camoufox browser instance and maintains the ref registry between commands. The daemon auto-starts on the first command and auto-stops after 30 minutes of inactivity.
|
|
251
|
+
|
|
252
|
+
## Related
|
|
253
|
+
|
|
254
|
+
- [google-analytics-cli](https://github.com/Bin-Huang/google-analytics-cli) -- Google Analytics CLI & Skills for AI agents (and humans)
|
|
255
|
+
- [google-search-console-cli](https://github.com/Bin-Huang/google-search-console-cli) -- Google Search Console CLI & Skills for AI agents (and humans)
|
|
256
|
+
- [youtube-analytics-cli](https://github.com/Bin-Huang/youtube-analytics-cli) -- YouTube Analytics CLI & Skills for AI agents (and humans)
|
|
257
|
+
- [x-analytics-cli](https://github.com/Bin-Huang/x-analytics-cli) -- X Analytics CLI & Skills for AI agents (and humans)
|
|
258
|
+
- [google-ads-open-cli](https://github.com/Bin-Huang/google-ads-open-cli) -- Google Ads CLI & Skills for AI agents (and humans)
|
|
259
|
+
- [meta-ads-open-cli](https://github.com/Bin-Huang/meta-ads-open-cli) -- Meta Ads CLI & Skills for AI agents (and humans)
|
|
260
|
+
- [microsoft-ads-cli](https://github.com/Bin-Huang/microsoft-ads-cli) -- Microsoft Ads CLI & Skills for AI agents (and humans)
|
|
261
|
+
- [amazon-ads-open-cli](https://github.com/Bin-Huang/amazon-ads-open-cli) -- Amazon Ads CLI & Skills for AI agents (and humans)
|
|
262
|
+
- [tiktok-ads-cli](https://github.com/Bin-Huang/tiktok-ads-cli) -- TikTok Ads CLI & Skills for AI agents (and humans)
|
|
263
|
+
- [linkedin-ads-cli](https://github.com/Bin-Huang/linkedin-ads-cli) -- LinkedIn Ads CLI & Skills for AI agents (and humans)
|
|
264
|
+
- [x-ads-cli](https://github.com/Bin-Huang/x-ads-cli) -- X Ads CLI & Skills for AI agents (and humans)
|
|
265
|
+
- [snapchat-ads-cli](https://github.com/Bin-Huang/snapchat-ads-cli) -- Snapchat Ads CLI & Skills for AI agents (and humans)
|
|
266
|
+
- [pinterest-ads-cli](https://github.com/Bin-Huang/pinterest-ads-cli) -- Pinterest Ads CLI & Skills for AI agents (and humans)
|
|
267
|
+
- [reddit-ads-cli](https://github.com/Bin-Huang/reddit-ads-cli) -- Reddit Ads CLI & Skills for AI agents (and humans)
|
|
268
|
+
- [spotify-ads-cli](https://github.com/Bin-Huang/spotify-ads-cli) -- Spotify Ads CLI & Skills for AI agents (and humans)
|
|
269
|
+
- [apple-ads-cli](https://github.com/Bin-Huang/apple-ads-cli) -- Apple Ads CLI & Skills for AI agents (and humans)
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Benn Huang
|
|
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.
|