@sleepwalkerai/cli 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/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # Sleepwalker CLI
2
+
3
+ Command-line client for the Sleepwalker API.
4
+
5
+ This package is intentionally a thin API client. It does not run Sleepwalker
6
+ analysis locally.
7
+
8
+ Public package:
9
+
10
+ ```text
11
+ @sleepwalkerai/cli
12
+ ```
13
+
14
+ Install it from npm or run it directly with `npx`.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install -g @sleepwalkerai/cli
20
+ sleepwalker init
21
+ sleepwalker auth key set sw_api_live_...
22
+ sleepwalker menu
23
+ sleepwalker doctor
24
+ ```
25
+
26
+ One-off usage:
27
+
28
+ ```bash
29
+ npx @sleepwalkerai/cli doctor
30
+ ```
31
+
32
+ ## Local Usage
33
+
34
+ From this repository:
35
+
36
+ ```bash
37
+ cd packages/cli
38
+ node ./bin/sleepwalker.js
39
+ node ./bin/sleepwalker.js menu
40
+ node ./bin/sleepwalker.js init
41
+ node ./bin/sleepwalker.js commands
42
+ ```
43
+
44
+ With an API key:
45
+
46
+ ```bash
47
+ export SLEEPWALKER_API_KEY=sw_api_live_...
48
+ node ./bin/sleepwalker.js credits
49
+ ```
50
+
51
+ Or store the key locally:
52
+
53
+ ```bash
54
+ node ./bin/sleepwalker.js auth key set sw_api_live_...
55
+ node ./bin/sleepwalker.js doctor
56
+ ```
57
+
58
+ To point the CLI at a non-production API base:
59
+
60
+ ```bash
61
+ node ./bin/sleepwalker.js config set api-base-url https://api.sleepwalker.ai
62
+ node ./bin/sleepwalker.js config show
63
+ ```
64
+
65
+ ## Examples
66
+
67
+ ```bash
68
+ sleepwalker init
69
+ sleepwalker menu
70
+ sleepwalker doctor
71
+ sleepwalker page serialize https://www.sleepwalker.ai
72
+ sleepwalker reports by-url https://www.sleepwalker.ai
73
+ sleepwalker visibility run https://www.sleepwalker.ai --brand Sleepwalker --prompt "What are the best AI visibility tools?" --platform perplexity --watch
74
+ sleepwalker ci score https://www.sleepwalker.ai
75
+ sleepwalker ci run https://www.sleepwalker.ai --depth full --watch
76
+ sleepwalker activity list
77
+ ```
78
+
79
+ Add `--json` to print raw API responses.
80
+
81
+ For retryable scripts, pass your own idempotency key on persisted run creation:
82
+
83
+ ```bash
84
+ sleepwalker visibility run https://www.sleepwalker.ai \
85
+ --brand Sleepwalker \
86
+ --prompt "What are the best AI visibility tools?" \
87
+ --platform perplexity \
88
+ --idempotency-key visibility-sleepwalker-homepage-001
89
+ ```
90
+
91
+ `--watch` polls for completion and stops after 15 minutes by default. Override
92
+ that with `--max-wait-seconds <seconds>` when a workflow needs a shorter or
93
+ longer wait.
94
+
95
+ ## Smoke Tests
96
+
97
+ Local command smoke:
98
+
99
+ ```bash
100
+ npm run smoke
101
+ ```
102
+
103
+ Read-only live smoke with a real API key:
104
+
105
+ ```bash
106
+ export SLEEPWALKER_API_KEY=sw_api_live_...
107
+ npm run smoke:live
108
+ ```
109
+
110
+ The live smoke does not run billable actions by default. To include one
111
+ intentional serialization action:
112
+
113
+ ```bash
114
+ SLEEPWALKER_CLI_SMOKE_WRITE=1 npm run smoke:live
115
+ ```
116
+
117
+ ## Terminal Color
118
+
119
+ Human output uses Sleepwalker-inspired terminal color when the terminal supports
120
+ it:
121
+
122
+ - green for successful AI Visibility / completed states;
123
+ - purple for Content Intelligence;
124
+ - cyan/blue for IDs, URLs, and API paths;
125
+ - yellow/red for queued, blocked, or failed work.
126
+
127
+ Colors are never added to `--json` output. Set `NO_COLOR=1` to disable styling
128
+ or `FORCE_COLOR=1` to force it in supported terminals.
129
+
130
+ If your current shell exports `NO_COLOR`, remove it while previewing:
131
+
132
+ ```bash
133
+ env -u NO_COLOR FORCE_COLOR=1 node ./bin/sleepwalker.js --help
134
+ ```
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from "../src/cli.js";
4
+
5
+ main(process.argv.slice(2), {
6
+ stdin: process.stdin,
7
+ stdout: process.stdout,
8
+ stderr: process.stderr,
9
+ env: process.env,
10
+ fetch: globalThis.fetch,
11
+ }).catch((error) => {
12
+ const message = error && error.message ? error.message : String(error);
13
+ process.stderr.write(`Sleepwalker CLI error: ${message}\n`);
14
+ process.exitCode = typeof error.exitCode === "number" ? error.exitCode : 1;
15
+ });
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@sleepwalkerai/cli",
3
+ "version": "0.1.0",
4
+ "description": "Command-line client for the Sleepwalker API.",
5
+ "type": "module",
6
+ "bin": {
7
+ "sleepwalker": "bin/sleepwalker.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node --test",
11
+ "smoke": "node ./bin/sleepwalker.js --help",
12
+ "smoke:live": "node ./scripts/smoke.js"
13
+ },
14
+ "engines": {
15
+ "node": ">=18.17"
16
+ },
17
+ "files": [
18
+ "bin",
19
+ "src",
20
+ "scripts",
21
+ "README.md"
22
+ ],
23
+ "license": "MIT"
24
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { execFile } from "node:child_process";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+
7
+ const execFileAsync = promisify(execFile);
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const cliPath = path.resolve(__dirname, "../bin/sleepwalker.js");
10
+
11
+ async function run(args, { env = process.env } = {}) {
12
+ const label = `sleepwalker ${args.join(" ")}`;
13
+ process.stdout.write(`\n> ${label}\n`);
14
+ const { stdout, stderr } = await execFileAsync(process.execPath, [cliPath, ...args], {
15
+ env: { ...env, NO_COLOR: "1" },
16
+ maxBuffer: 1024 * 1024 * 8,
17
+ });
18
+ if (stdout.trim()) {
19
+ process.stdout.write(`${stdout.trim()}\n`);
20
+ }
21
+ if (stderr.trim()) {
22
+ process.stderr.write(`${stderr.trim()}\n`);
23
+ }
24
+ }
25
+
26
+ async function main() {
27
+ await run(["--help"]);
28
+
29
+ if (!process.env.SLEEPWALKER_API_KEY) {
30
+ process.stdout.write("\nNo SLEEPWALKER_API_KEY set. Live read smoke skipped.\n");
31
+ process.stdout.write("Set SLEEPWALKER_API_KEY to smoke-test the hosted API without spending credits.\n");
32
+ return;
33
+ }
34
+
35
+ await run(["doctor", "--json"]);
36
+ await run(["credits", "--json"]);
37
+ await run(["tests", "list", "--limit", "1", "--json"]);
38
+ await run(["activity", "list", "--limit", "1", "--json"]);
39
+
40
+ if (process.env.SLEEPWALKER_CLI_SMOKE_WRITE === "1") {
41
+ const url = process.env.SLEEPWALKER_CLI_SMOKE_WRITE_URL || "https://www.sleepwalker.ai";
42
+ await run(["page", "serialize", url, "--max-chars", "1000", "--json"]);
43
+ } else {
44
+ process.stdout.write("\nWrite smoke skipped. Set SLEEPWALKER_CLI_SMOKE_WRITE=1 to include a billable action.\n");
45
+ }
46
+ }
47
+
48
+ main().catch((error) => {
49
+ process.stderr.write(`\nSmoke failed: ${error.message}\n`);
50
+ if (error.stdout) {
51
+ process.stderr.write(`${error.stdout}\n`);
52
+ }
53
+ if (error.stderr) {
54
+ process.stderr.write(`${error.stderr}\n`);
55
+ }
56
+ process.exitCode = 1;
57
+ });
package/src/args.js ADDED
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+
3
+ export function parseFlags(args) {
4
+ const positional = [];
5
+ const flags = {};
6
+
7
+ for (let index = 0; index < args.length; index += 1) {
8
+ const arg = args[index];
9
+ if (!arg.startsWith("--") || arg === "--") {
10
+ positional.push(arg);
11
+ continue;
12
+ }
13
+
14
+ const withoutPrefix = arg.slice(2);
15
+ const [rawKey, inlineValue] = withoutPrefix.split(/=(.*)/s);
16
+ const key = rawKey.trim();
17
+ if (!key) {
18
+ continue;
19
+ }
20
+
21
+ let value = inlineValue;
22
+ if (value === undefined) {
23
+ const next = args[index + 1];
24
+ if (next !== undefined && !next.startsWith("--")) {
25
+ value = next;
26
+ index += 1;
27
+ } else {
28
+ value = true;
29
+ }
30
+ }
31
+
32
+ if (flags[key] === undefined) {
33
+ flags[key] = value;
34
+ } else if (Array.isArray(flags[key])) {
35
+ flags[key].push(value);
36
+ } else {
37
+ flags[key] = [flags[key], value];
38
+ }
39
+ }
40
+
41
+ return { positional, flags };
42
+ }
43
+
44
+ export function flagList(flags, name) {
45
+ const value = flags[name];
46
+ if (value === undefined || value === false) {
47
+ return [];
48
+ }
49
+ return Array.isArray(value) ? value.map(String) : [String(value)];
50
+ }
51
+
52
+ export function flagString(flags, name, fallback = "") {
53
+ const value = flags[name];
54
+ if (value === undefined || value === true || value === false) {
55
+ return fallback;
56
+ }
57
+ return Array.isArray(value) ? String(value[value.length - 1]) : String(value);
58
+ }
59
+
60
+ export function flagNumber(flags, name, fallback) {
61
+ const value = flagString(flags, name, "");
62
+ if (!value) {
63
+ return fallback;
64
+ }
65
+ const parsed = Number(value);
66
+ return Number.isFinite(parsed) ? parsed : fallback;
67
+ }
68
+
69
+ export function flagBool(flags, name) {
70
+ return Boolean(flags[name]);
71
+ }
72
+
73
+ export function readLinesFromFile(filePath) {
74
+ const raw = fs.readFileSync(filePath, "utf8");
75
+ return raw
76
+ .split(/\r?\n/)
77
+ .map((line) => line.trim())
78
+ .filter(Boolean);
79
+ }