stpr 1.0.5 → 1.0.6
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 +159 -0
- package/dist/cli.js +12 -23
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# stpr — Stepper Skills CLI
|
|
2
|
+
|
|
3
|
+
Skill Sets are a Stepper feature that let you bundle integration actions into curated, authenticated toolkits — and expose them to AI agents, CLIs, and any MCP-compatible client.
|
|
4
|
+
|
|
5
|
+
`stpr` is a command-line interface for interacting with [Stepper](https://stepper.io) Skill Sets. Discover, inspect, and execute integration actions directly from your terminal. This is perfect for OpenClaw or agents that can interact with a CLI, or for developers who want to use skills in their own scripts.
|
|
6
|
+
|
|
7
|
+
If you prefer, you can directly use the Stepper MCP server at `https://mcp.stepper.io/skill-sets/mcp`instead of the CLI.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g stpr
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Authentication
|
|
16
|
+
|
|
17
|
+
The CLI supports three authentication methods:
|
|
18
|
+
|
|
19
|
+
### OAuth Login (recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
stpr login
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Opens your browser to authenticate and select a Skill Set. Credentials are saved to `~/.config/stepper-skillsets/config.json` and automatically refreshed when they expire.
|
|
26
|
+
|
|
27
|
+
### Static Token
|
|
28
|
+
|
|
29
|
+
Generate a token from [Stepper](https://app.stepper.io/flow/skill-sets) and pass it directly:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
stpr --token sst_your_token_here list
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Environment Variable
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
export STEPPER_SKILL_TOKEN=sst_your_token_here
|
|
39
|
+
stpr list
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
### Profile Management
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
stpr login # Authenticate via OAuth (opens browser)
|
|
48
|
+
stpr logout [name] # Remove a saved profile, or all profiles if no name given
|
|
49
|
+
stpr profiles # List all saved profiles
|
|
50
|
+
stpr use <name> # Switch the active profile
|
|
51
|
+
stpr whoami # Show active profile and server info
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Discovering Skills
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
stpr list # List all available skills, grouped by service
|
|
58
|
+
stpr list --verbose # Include full input schemas
|
|
59
|
+
stpr list <service> # List skills for a specific service
|
|
60
|
+
stpr <service> # Shorthand for listing a service's skills
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Inspecting Parameters
|
|
64
|
+
|
|
65
|
+
Many skills have dynamic parameters — fields that change based on the values of other fields. Calling a skill without `--call` returns its current parameter schema.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# See what fields are needed for add_row, given a spreadsheet
|
|
69
|
+
stpr google-sheets add_row -i '{"spreadsheet_id": "abc123"}'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Some parameters have dynamic dropdown options. Fetch them with `--options`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
stpr google-sheets add_row --options worksheet_id \
|
|
76
|
+
-i '{"spreadsheet_id": "abc123"}' \
|
|
77
|
+
--search "Sheet" \
|
|
78
|
+
--cursor "next_page"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Calling Skills
|
|
82
|
+
|
|
83
|
+
Use the `--call` flag to execute an action:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
stpr google-sheets create_sheet --call \
|
|
87
|
+
-i '{"name": "Q1 Report", "columns": "Name, Email, Phone"}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Polling Async Results
|
|
91
|
+
|
|
92
|
+
Component library tools run asynchronously. Poll for results with:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
stpr status <statusId>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Input
|
|
99
|
+
|
|
100
|
+
Pass JSON input via the `-i` / `--input` flag or pipe it through stdin:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Flag
|
|
104
|
+
stpr slack send_message --call -i '{"channel": "#general", "text": "Hello!"}'
|
|
105
|
+
|
|
106
|
+
# Stdin
|
|
107
|
+
echo '{"channel": "#general", "text": "Hello!"}' | stpr slack send_message --call
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Options Reference
|
|
111
|
+
|
|
112
|
+
| Flag | Description |
|
|
113
|
+
| -------------------- | --------------------------------------------------------------- |
|
|
114
|
+
| `--token <token>` | Auth token (overrides saved profiles and `STEPPER_SKILL_TOKEN`) |
|
|
115
|
+
| `--base-url <url>` | Override MCP server URL (default: `https://mcp.stepper.io`) |
|
|
116
|
+
| `--skillset <name>` | Use a specific saved profile instead of the active one |
|
|
117
|
+
| `--call` | Execute the skill (default behavior is parameter inspection) |
|
|
118
|
+
| `--verbose` | Include full `inputSchema` when listing skills |
|
|
119
|
+
| `-i, --input <json>` | JSON input for calls, parameter fetches, or option queries |
|
|
120
|
+
| `--options <param>` | Fetch dynamic dropdown options for a parameter |
|
|
121
|
+
| `--search <query>` | Filter dropdown options by search term |
|
|
122
|
+
| `--cursor <cursor>` | Pagination cursor for dropdown options |
|
|
123
|
+
| `-h, --help` | Show help |
|
|
124
|
+
| `-v, --version` | Show version |
|
|
125
|
+
|
|
126
|
+
## Environment Variables
|
|
127
|
+
|
|
128
|
+
| Variable | Description |
|
|
129
|
+
| --------------------- | ------------------------------------------------------------- |
|
|
130
|
+
| `STEPPER_SKILL_TOKEN` | Auth token (used when no `--token` flag and no saved profile) |
|
|
131
|
+
| `STEPPER_URL` | Override the MCP server base URL |
|
|
132
|
+
|
|
133
|
+
## Examples
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Authenticate
|
|
137
|
+
stpr login
|
|
138
|
+
|
|
139
|
+
# List everything available
|
|
140
|
+
stpr list
|
|
141
|
+
|
|
142
|
+
# Explore a service
|
|
143
|
+
stpr google-sheets
|
|
144
|
+
|
|
145
|
+
# Inspect dynamic parameters step by step
|
|
146
|
+
stpr google-sheets add_row -i '{}'
|
|
147
|
+
stpr google-sheets add_row -i '{"spreadsheet_id": "abc123"}'
|
|
148
|
+
|
|
149
|
+
# Fetch dropdown options
|
|
150
|
+
stpr google-sheets add_row --options worksheet_id -i '{"spreadsheet_id": "abc123"}'
|
|
151
|
+
|
|
152
|
+
# Execute
|
|
153
|
+
stpr google-sheets add_row --call -i '{"spreadsheet_id": "abc123", "worksheet_id": "Sheet1", "values": {"Name": "Alice"}}'
|
|
154
|
+
|
|
155
|
+
# Switch between skill sets
|
|
156
|
+
stpr profiles
|
|
157
|
+
stpr use "Production Tools"
|
|
158
|
+
stpr list
|
|
159
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -138,7 +138,6 @@ var OAUTH_CALLBACK_PORT = 3847;
|
|
|
138
138
|
var CALLBACK_PATH = "/callback";
|
|
139
139
|
var CONFIG_DIR = path.join(os.homedir(), ".config", "stepper-skillsets");
|
|
140
140
|
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
141
|
-
var LEGACY_CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
142
141
|
function generatePKCE() {
|
|
143
142
|
const codeVerifier = crypto.randomBytes(32).toString("base64url");
|
|
144
143
|
const codeChallenge = crypto.createHash("sha256").update(codeVerifier).digest().toString("base64url");
|
|
@@ -158,29 +157,14 @@ function saveConfig(config) {
|
|
|
158
157
|
mode: 384
|
|
159
158
|
});
|
|
160
159
|
}
|
|
161
|
-
function migrateLegacyCredentials() {
|
|
162
|
-
try {
|
|
163
|
-
const data = fs.readFileSync(LEGACY_CREDENTIALS_FILE, "utf-8");
|
|
164
|
-
const creds = JSON.parse(data);
|
|
165
|
-
const config = {
|
|
166
|
-
active: "default",
|
|
167
|
-
skillsets: { default: creds }
|
|
168
|
-
};
|
|
169
|
-
saveConfig(config);
|
|
170
|
-
fs.unlinkSync(LEGACY_CREDENTIALS_FILE);
|
|
171
|
-
} catch {
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
160
|
function getConfigPathForDisplay() {
|
|
175
161
|
return CONFIG_FILE;
|
|
176
162
|
}
|
|
177
163
|
function getActiveSkillset() {
|
|
178
|
-
migrateLegacyCredentials();
|
|
179
164
|
const config = loadConfig();
|
|
180
165
|
return config.active;
|
|
181
166
|
}
|
|
182
167
|
function setActiveSkillset(name) {
|
|
183
|
-
migrateLegacyCredentials();
|
|
184
168
|
const config = loadConfig();
|
|
185
169
|
if (!(name in config.skillsets)) {
|
|
186
170
|
return false;
|
|
@@ -190,7 +174,6 @@ function setActiveSkillset(name) {
|
|
|
190
174
|
return true;
|
|
191
175
|
}
|
|
192
176
|
function listSkillsets() {
|
|
193
|
-
migrateLegacyCredentials();
|
|
194
177
|
const config = loadConfig();
|
|
195
178
|
return Object.entries(config.skillsets).map(([name, creds]) => ({
|
|
196
179
|
name,
|
|
@@ -199,12 +182,10 @@ function listSkillsets() {
|
|
|
199
182
|
}));
|
|
200
183
|
}
|
|
201
184
|
function getCredentials(name) {
|
|
202
|
-
migrateLegacyCredentials();
|
|
203
185
|
const config = loadConfig();
|
|
204
186
|
return config.skillsets[name] ?? null;
|
|
205
187
|
}
|
|
206
188
|
function saveCredentials(name, creds) {
|
|
207
|
-
migrateLegacyCredentials();
|
|
208
189
|
const config = loadConfig();
|
|
209
190
|
config.skillsets[name] = creds;
|
|
210
191
|
if (!config.active || !(config.active in config.skillsets)) {
|
|
@@ -213,7 +194,6 @@ function saveCredentials(name, creds) {
|
|
|
213
194
|
saveConfig(config);
|
|
214
195
|
}
|
|
215
196
|
function deleteSkillset(name) {
|
|
216
|
-
migrateLegacyCredentials();
|
|
217
197
|
const config = loadConfig();
|
|
218
198
|
if (!(name in config.skillsets)) {
|
|
219
199
|
return false;
|
|
@@ -226,7 +206,6 @@ function deleteSkillset(name) {
|
|
|
226
206
|
return true;
|
|
227
207
|
}
|
|
228
208
|
function deleteAllSkillsets() {
|
|
229
|
-
migrateLegacyCredentials();
|
|
230
209
|
const config = loadConfig();
|
|
231
210
|
const count = Object.keys(config.skillsets).length;
|
|
232
211
|
config.skillsets = {};
|
|
@@ -320,6 +299,18 @@ async function runLoginFlow(baseUrl) {
|
|
|
320
299
|
res.end("Not found");
|
|
321
300
|
return;
|
|
322
301
|
}
|
|
302
|
+
const requestHost = req.headers.host ?? `127.0.0.1:${OAUTH_CALLBACK_PORT}`;
|
|
303
|
+
const callbackUrl = new URL(req.url ?? "/", `http://${requestHost}`);
|
|
304
|
+
const receivedCallbackUrl = `${callbackUrl.origin}${callbackUrl.pathname}`;
|
|
305
|
+
if (receivedCallbackUrl !== redirectUri) {
|
|
306
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
307
|
+
res.end(
|
|
308
|
+
"<html><body><h1>Login failed</h1><p>Redirect URI mismatch</p><p>You can close this tab.</p></body></html>"
|
|
309
|
+
);
|
|
310
|
+
server.close();
|
|
311
|
+
reject(new Error("OAuth redirect URI mismatch"));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
323
314
|
const code = url.searchParams.get("code");
|
|
324
315
|
const returnedState = url.searchParams.get("state");
|
|
325
316
|
const error = url.searchParams.get("error");
|
|
@@ -412,7 +403,6 @@ async function refreshAccessToken(baseUrl, clientId, refreshToken) {
|
|
|
412
403
|
};
|
|
413
404
|
}
|
|
414
405
|
async function getValidToken(skillsetName, baseUrl) {
|
|
415
|
-
migrateLegacyCredentials();
|
|
416
406
|
const config = loadConfig();
|
|
417
407
|
const name = skillsetName ?? config.active;
|
|
418
408
|
if (!name || !(name in config.skillsets)) {
|
|
@@ -575,7 +565,6 @@ Options:
|
|
|
575
565
|
-v, --version Show version
|
|
576
566
|
|
|
577
567
|
Examples:
|
|
578
|
-
|
|
579
568
|
Auth (optional, can use STEPPER_SKILL_TOKEN env var, or --token <token> from https://app.stepper.io/flow/skill-sets):
|
|
580
569
|
stpr login
|
|
581
570
|
|