@krl-grn/wande 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 +95 -0
- package/dist/client.js +27 -0
- package/dist/config.js +69 -0
- package/dist/index.js +290 -0
- package/dist/output.js +17 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# wande CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for automating your Wande Task Manager workspace through bot tokens.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- A valid bot token generated in **Settings -> Bot API**
|
|
9
|
+
- Your Convex HTTP URL (for example: `https://<deployment>.convex.site`)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g @krl-grn/wande
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
wande login <BOT_TOKEN>
|
|
21
|
+
wande whoami --json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Core commands
|
|
25
|
+
|
|
26
|
+
### Tasks
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
wande tasks:list --limit 20 --json
|
|
30
|
+
wande tasks:create --title "Task from CLI" --status new --json
|
|
31
|
+
wande tasks:status --id <TASK_ID> --status done --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Routines
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
wande routines:list --limit 20 --json
|
|
38
|
+
wande routines:create --title "Routine from CLI" --status new --json
|
|
39
|
+
wande routines:status --id <ROUTINE_ID> --status done --json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Projects
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
wande projects:list --json
|
|
46
|
+
wande projects:create --title "CLI Project" --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Queue instances
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
wande queue:list --date 2026-02-23 --json
|
|
53
|
+
wande queue:create --date 2026-02-23 --entity-id e1 --entity-type task --title "Queue item" --status new --json
|
|
54
|
+
wande queue:status --instance-id <INSTANCE_ID> --status done --json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Security notes
|
|
58
|
+
|
|
59
|
+
- Bot tokens are shown only once when created. Save them securely.
|
|
60
|
+
- If a token is exposed, revoke it immediately in **Settings -> Bot API** and create a new one.
|
|
61
|
+
- The CLI stores token config locally for your user profile.
|
|
62
|
+
|
|
63
|
+
## API behavior and limits
|
|
64
|
+
|
|
65
|
+
- Supported entities: `tasks`, `routines`, `projects`, `queueInstances`
|
|
66
|
+
- Bot is allowed to:
|
|
67
|
+
- read tasks/routines/projects/queueInstances
|
|
68
|
+
- create tasks/routines/projects/queueInstances
|
|
69
|
+
- update status for tasks/routines/queueInstances
|
|
70
|
+
- Bot is not allowed to:
|
|
71
|
+
- delete entities
|
|
72
|
+
- reorder entities
|
|
73
|
+
- change date/time/order for existing queue instances
|
|
74
|
+
- Rate limits (server-side):
|
|
75
|
+
- `60 req/min` per token
|
|
76
|
+
- `20 create/min` per token
|
|
77
|
+
- `200 create/day` per token
|
|
78
|
+
|
|
79
|
+
## Troubleshooting
|
|
80
|
+
|
|
81
|
+
- `401 UNAUTHORIZED`: token is invalid, expired, or revoked.
|
|
82
|
+
- `403 FORBIDDEN`: token is missing required scopes.
|
|
83
|
+
- `429 RATE_LIMITED`: you exceeded rate limits; retry later.
|
|
84
|
+
- `Could not find public function ...`: run Convex dev/deploy so bot functions are available.
|
|
85
|
+
- `login` resolves URL automatically from `https://wande.app/api/cli/config`. If needed, override with `--url`.
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
From repository root:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run cli:install
|
|
93
|
+
npm run cli:build
|
|
94
|
+
node packages/cli/dist/index.js --help
|
|
95
|
+
```
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class BotApiClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
constructor(baseUrl) {
|
|
4
|
+
this.baseUrl = baseUrl;
|
|
5
|
+
}
|
|
6
|
+
async request(path, options) {
|
|
7
|
+
const headers = {
|
|
8
|
+
Authorization: `Bearer ${options.token}`,
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
};
|
|
11
|
+
if (options.idempotencyKey) {
|
|
12
|
+
headers["X-Idempotency-Key"] = options.idempotencyKey;
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
15
|
+
method: options.method ?? "GET",
|
|
16
|
+
headers,
|
|
17
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
18
|
+
});
|
|
19
|
+
const raw = await response.text();
|
|
20
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const message = parsed?.error?.message ?? parsed?.message ?? `Request failed (${response.status})`;
|
|
23
|
+
throw new Error(message);
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
function configDir() {
|
|
5
|
+
return path.join(os.homedir(), ".taskmanager-cli");
|
|
6
|
+
}
|
|
7
|
+
function configPath() {
|
|
8
|
+
return path.join(configDir(), "config.json");
|
|
9
|
+
}
|
|
10
|
+
export async function loadConfig() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await readFile(configPath(), "utf8");
|
|
13
|
+
return JSON.parse(raw);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function saveConfig(next) {
|
|
20
|
+
await mkdir(configDir(), { recursive: true });
|
|
21
|
+
await writeFile(configPath(), JSON.stringify(next, null, 2), "utf8");
|
|
22
|
+
}
|
|
23
|
+
export async function clearToken() {
|
|
24
|
+
const cfg = await loadConfig();
|
|
25
|
+
delete cfg.token;
|
|
26
|
+
await saveConfig(cfg);
|
|
27
|
+
}
|
|
28
|
+
export function defaultBaseUrl() {
|
|
29
|
+
return process.env.TASKMANAGER_BOT_BASE_URL ?? "http://127.0.0.1:3210";
|
|
30
|
+
}
|
|
31
|
+
function normalizeBaseUrl(input) {
|
|
32
|
+
return input.replace(/\/+$/, "");
|
|
33
|
+
}
|
|
34
|
+
function candidateSiteUrls() {
|
|
35
|
+
const candidates = [
|
|
36
|
+
process.env.TASKMANAGER_SITE_URL,
|
|
37
|
+
"https://wande.app",
|
|
38
|
+
].filter((value) => Boolean(value && value.trim().length > 0));
|
|
39
|
+
return Array.from(new Set(candidates.map(normalizeBaseUrl)));
|
|
40
|
+
}
|
|
41
|
+
export async function resolveBotBaseUrl(explicitUrl, existingUrl) {
|
|
42
|
+
if (explicitUrl) {
|
|
43
|
+
return normalizeBaseUrl(explicitUrl);
|
|
44
|
+
}
|
|
45
|
+
if (existingUrl) {
|
|
46
|
+
return normalizeBaseUrl(existingUrl);
|
|
47
|
+
}
|
|
48
|
+
if (process.env.TASKMANAGER_BOT_BASE_URL) {
|
|
49
|
+
return normalizeBaseUrl(process.env.TASKMANAGER_BOT_BASE_URL);
|
|
50
|
+
}
|
|
51
|
+
for (const siteUrl of candidateSiteUrls()) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`${siteUrl}/api/cli/config`, {
|
|
54
|
+
method: "GET",
|
|
55
|
+
});
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const json = (await response.json());
|
|
60
|
+
if (json.botBaseUrl && json.botBaseUrl.trim().length > 0) {
|
|
61
|
+
return normalizeBaseUrl(json.botBaseUrl);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Ignore bootstrap failures and continue trying fallbacks.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return defaultBaseUrl();
|
|
69
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { BotApiClient } from "./client.js";
|
|
5
|
+
import { clearToken, loadConfig, resolveBotBaseUrl, saveConfig } from "./config.js";
|
|
6
|
+
import { printData } from "./output.js";
|
|
7
|
+
async function getRuntime() {
|
|
8
|
+
const config = await loadConfig();
|
|
9
|
+
const baseUrl = await resolveBotBaseUrl(undefined, config.baseUrl);
|
|
10
|
+
const token = config.token;
|
|
11
|
+
if (!token) {
|
|
12
|
+
throw new Error("Not authenticated. Run: wande login <token>");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
config,
|
|
16
|
+
token,
|
|
17
|
+
client: new BotApiClient(baseUrl),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async function performLogin(token, explicitUrl) {
|
|
21
|
+
const current = await loadConfig();
|
|
22
|
+
const baseUrl = await resolveBotBaseUrl(explicitUrl, current.baseUrl);
|
|
23
|
+
await saveConfig({
|
|
24
|
+
...current,
|
|
25
|
+
token,
|
|
26
|
+
baseUrl,
|
|
27
|
+
});
|
|
28
|
+
console.log(`Saved bot token (base URL: ${baseUrl})`);
|
|
29
|
+
}
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program.name("wande").description("Wande bot CLI");
|
|
32
|
+
program
|
|
33
|
+
.command("login <token>")
|
|
34
|
+
.description("Save bot token and auto-resolve API URL")
|
|
35
|
+
.option("--url <baseUrl>")
|
|
36
|
+
.action(async (token, options) => {
|
|
37
|
+
await performLogin(token, options.url);
|
|
38
|
+
});
|
|
39
|
+
program
|
|
40
|
+
.command("whoami")
|
|
41
|
+
.description("Show authenticated bot identity")
|
|
42
|
+
.option("--json", "JSON output", false)
|
|
43
|
+
.action(async (opts) => {
|
|
44
|
+
const { client, token } = await getRuntime();
|
|
45
|
+
const res = await client.request("/bot/auth/whoami", {
|
|
46
|
+
method: "POST",
|
|
47
|
+
token,
|
|
48
|
+
body: {},
|
|
49
|
+
});
|
|
50
|
+
printData(res.data, Boolean(opts.json));
|
|
51
|
+
});
|
|
52
|
+
program
|
|
53
|
+
.command("logout")
|
|
54
|
+
.description("Remove saved bot token")
|
|
55
|
+
.action(async () => {
|
|
56
|
+
await clearToken();
|
|
57
|
+
console.log("Token removed");
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command("auth:login")
|
|
61
|
+
.description("Legacy alias for login")
|
|
62
|
+
.requiredOption("--token <token>")
|
|
63
|
+
.option("--url <baseUrl>")
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
await performLogin(options.token, options.url);
|
|
66
|
+
});
|
|
67
|
+
program.command("auth:logout").description("Legacy alias for logout").action(async () => {
|
|
68
|
+
await clearToken();
|
|
69
|
+
console.log("Token removed");
|
|
70
|
+
});
|
|
71
|
+
program.command("auth:whoami").description("Legacy alias for whoami").option("--json", "JSON output", false).action(async (opts) => {
|
|
72
|
+
const { client, token } = await getRuntime();
|
|
73
|
+
const res = await client.request("/bot/auth/whoami", {
|
|
74
|
+
method: "POST",
|
|
75
|
+
token,
|
|
76
|
+
body: {},
|
|
77
|
+
});
|
|
78
|
+
printData(res.data, Boolean(opts.json));
|
|
79
|
+
});
|
|
80
|
+
program
|
|
81
|
+
.command("tasks:list")
|
|
82
|
+
.option("--status <status>")
|
|
83
|
+
.option("--limit <limit>")
|
|
84
|
+
.option("--json", "JSON output", false)
|
|
85
|
+
.action(async (opts) => {
|
|
86
|
+
const { client, token } = await getRuntime();
|
|
87
|
+
const params = new URLSearchParams();
|
|
88
|
+
if (opts.status)
|
|
89
|
+
params.set("status", String(opts.status));
|
|
90
|
+
if (opts.limit)
|
|
91
|
+
params.set("limit", String(opts.limit));
|
|
92
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
93
|
+
const res = await client.request(`/bot/tasks${suffix}`, {
|
|
94
|
+
token,
|
|
95
|
+
});
|
|
96
|
+
printData(res.data, Boolean(opts.json));
|
|
97
|
+
});
|
|
98
|
+
program
|
|
99
|
+
.command("tasks:create")
|
|
100
|
+
.requiredOption("--title <title>")
|
|
101
|
+
.option("--content <content>")
|
|
102
|
+
.option("--project-id <projectId>")
|
|
103
|
+
.option("--status <status>")
|
|
104
|
+
.option("--idempotency-key <key>")
|
|
105
|
+
.option("--json", "JSON output", false)
|
|
106
|
+
.action(async (opts) => {
|
|
107
|
+
const { client, token } = await getRuntime();
|
|
108
|
+
const res = await client.request("/bot/tasks", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
token,
|
|
111
|
+
idempotencyKey: opts.idempotencyKey ?? randomUUID(),
|
|
112
|
+
body: {
|
|
113
|
+
title: opts.title,
|
|
114
|
+
content: opts.content,
|
|
115
|
+
project_id: opts.projectId,
|
|
116
|
+
status: opts.status,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
printData(res.data, Boolean(opts.json));
|
|
120
|
+
});
|
|
121
|
+
program
|
|
122
|
+
.command("tasks:status")
|
|
123
|
+
.requiredOption("--id <taskId>")
|
|
124
|
+
.requiredOption("--status <status>")
|
|
125
|
+
.option("--json", "JSON output", false)
|
|
126
|
+
.action(async (opts) => {
|
|
127
|
+
const { client, token } = await getRuntime();
|
|
128
|
+
const res = await client.request(`/bot/tasks/${opts.id}/status`, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
token,
|
|
131
|
+
body: { status: opts.status },
|
|
132
|
+
});
|
|
133
|
+
printData(res.data, Boolean(opts.json));
|
|
134
|
+
});
|
|
135
|
+
program
|
|
136
|
+
.command("routines:list")
|
|
137
|
+
.option("--status <status>")
|
|
138
|
+
.option("--limit <limit>")
|
|
139
|
+
.option("--json", "JSON output", false)
|
|
140
|
+
.action(async (opts) => {
|
|
141
|
+
const { client, token } = await getRuntime();
|
|
142
|
+
const params = new URLSearchParams();
|
|
143
|
+
if (opts.status)
|
|
144
|
+
params.set("status", String(opts.status));
|
|
145
|
+
if (opts.limit)
|
|
146
|
+
params.set("limit", String(opts.limit));
|
|
147
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
148
|
+
const res = await client.request(`/bot/routines${suffix}`, {
|
|
149
|
+
token,
|
|
150
|
+
});
|
|
151
|
+
printData(res.data, Boolean(opts.json));
|
|
152
|
+
});
|
|
153
|
+
program
|
|
154
|
+
.command("routines:create")
|
|
155
|
+
.requiredOption("--title <title>")
|
|
156
|
+
.option("--content <content>")
|
|
157
|
+
.option("--project-id <projectId>")
|
|
158
|
+
.option("--status <status>")
|
|
159
|
+
.option("--idempotency-key <key>")
|
|
160
|
+
.option("--json", "JSON output", false)
|
|
161
|
+
.action(async (opts) => {
|
|
162
|
+
const { client, token } = await getRuntime();
|
|
163
|
+
const res = await client.request("/bot/routines", {
|
|
164
|
+
method: "POST",
|
|
165
|
+
token,
|
|
166
|
+
idempotencyKey: opts.idempotencyKey ?? randomUUID(),
|
|
167
|
+
body: {
|
|
168
|
+
title: opts.title,
|
|
169
|
+
content: opts.content,
|
|
170
|
+
project_id: opts.projectId,
|
|
171
|
+
status: opts.status,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
printData(res.data, Boolean(opts.json));
|
|
175
|
+
});
|
|
176
|
+
program
|
|
177
|
+
.command("routines:status")
|
|
178
|
+
.requiredOption("--id <routineId>")
|
|
179
|
+
.requiredOption("--status <status>")
|
|
180
|
+
.option("--json", "JSON output", false)
|
|
181
|
+
.action(async (opts) => {
|
|
182
|
+
const { client, token } = await getRuntime();
|
|
183
|
+
const res = await client.request(`/bot/routines/${opts.id}/status`, {
|
|
184
|
+
method: "POST",
|
|
185
|
+
token,
|
|
186
|
+
body: { status: opts.status },
|
|
187
|
+
});
|
|
188
|
+
printData(res.data, Boolean(opts.json));
|
|
189
|
+
});
|
|
190
|
+
program
|
|
191
|
+
.command("projects:list")
|
|
192
|
+
.option("--limit <limit>")
|
|
193
|
+
.option("--json", "JSON output", false)
|
|
194
|
+
.action(async (opts) => {
|
|
195
|
+
const { client, token } = await getRuntime();
|
|
196
|
+
const params = new URLSearchParams();
|
|
197
|
+
if (opts.limit)
|
|
198
|
+
params.set("limit", String(opts.limit));
|
|
199
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
200
|
+
const res = await client.request(`/bot/projects${suffix}`, {
|
|
201
|
+
token,
|
|
202
|
+
});
|
|
203
|
+
printData(res.data, Boolean(opts.json));
|
|
204
|
+
});
|
|
205
|
+
program
|
|
206
|
+
.command("projects:create")
|
|
207
|
+
.requiredOption("--title <title>")
|
|
208
|
+
.option("--color <color>")
|
|
209
|
+
.option("--idempotency-key <key>")
|
|
210
|
+
.option("--json", "JSON output", false)
|
|
211
|
+
.action(async (opts) => {
|
|
212
|
+
const { client, token } = await getRuntime();
|
|
213
|
+
const res = await client.request("/bot/projects", {
|
|
214
|
+
method: "POST",
|
|
215
|
+
token,
|
|
216
|
+
idempotencyKey: opts.idempotencyKey ?? randomUUID(),
|
|
217
|
+
body: {
|
|
218
|
+
title: opts.title,
|
|
219
|
+
color: opts.color,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
printData(res.data, Boolean(opts.json));
|
|
223
|
+
});
|
|
224
|
+
program
|
|
225
|
+
.command("queue:list")
|
|
226
|
+
.option("--date <date>")
|
|
227
|
+
.option("--limit <limit>")
|
|
228
|
+
.option("--json", "JSON output", false)
|
|
229
|
+
.action(async (opts) => {
|
|
230
|
+
const { client, token } = await getRuntime();
|
|
231
|
+
const params = new URLSearchParams();
|
|
232
|
+
if (opts.date)
|
|
233
|
+
params.set("date", String(opts.date));
|
|
234
|
+
if (opts.limit)
|
|
235
|
+
params.set("limit", String(opts.limit));
|
|
236
|
+
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
237
|
+
const res = await client.request(`/bot/queue${suffix}`, {
|
|
238
|
+
token,
|
|
239
|
+
});
|
|
240
|
+
printData(res.data, Boolean(opts.json));
|
|
241
|
+
});
|
|
242
|
+
program
|
|
243
|
+
.command("queue:create")
|
|
244
|
+
.requiredOption("--date <date>")
|
|
245
|
+
.requiredOption("--entity-id <entityId>")
|
|
246
|
+
.requiredOption("--entity-type <entityType>")
|
|
247
|
+
.requiredOption("--title <title>")
|
|
248
|
+
.option("--project-id <projectId>")
|
|
249
|
+
.option("--description <description>")
|
|
250
|
+
.option("--status <status>", "new")
|
|
251
|
+
.option("--idempotency-key <key>")
|
|
252
|
+
.option("--json", "JSON output", false)
|
|
253
|
+
.action(async (opts) => {
|
|
254
|
+
const { client, token } = await getRuntime();
|
|
255
|
+
const res = await client.request("/bot/queue/instances", {
|
|
256
|
+
method: "POST",
|
|
257
|
+
token,
|
|
258
|
+
idempotencyKey: opts.idempotencyKey ?? randomUUID(),
|
|
259
|
+
body: {
|
|
260
|
+
date: opts.date,
|
|
261
|
+
instance: {
|
|
262
|
+
entity_id: opts.entityId,
|
|
263
|
+
entity_type: opts.entityType,
|
|
264
|
+
title: opts.title,
|
|
265
|
+
project_id: opts.projectId,
|
|
266
|
+
description: opts.description,
|
|
267
|
+
status: opts.status,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
printData(res.data, Boolean(opts.json));
|
|
272
|
+
});
|
|
273
|
+
program
|
|
274
|
+
.command("queue:status")
|
|
275
|
+
.requiredOption("--instance-id <instanceId>")
|
|
276
|
+
.requiredOption("--status <status>")
|
|
277
|
+
.option("--json", "JSON output", false)
|
|
278
|
+
.action(async (opts) => {
|
|
279
|
+
const { client, token } = await getRuntime();
|
|
280
|
+
const res = await client.request(`/bot/queue/instances/${opts.instanceId}/status`, {
|
|
281
|
+
method: "POST",
|
|
282
|
+
token,
|
|
283
|
+
body: { status: opts.status },
|
|
284
|
+
});
|
|
285
|
+
printData(res.data, Boolean(opts.json));
|
|
286
|
+
});
|
|
287
|
+
program.parseAsync().catch((error) => {
|
|
288
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
289
|
+
process.exitCode = 1;
|
|
290
|
+
});
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function printData(payload, asJson) {
|
|
2
|
+
if (asJson) {
|
|
3
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(payload)) {
|
|
7
|
+
if (payload.length === 0) {
|
|
8
|
+
console.log("No items");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (const item of payload) {
|
|
12
|
+
console.log(JSON.stringify(item));
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@krl-grn/wande",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md"
|
|
9
|
+
],
|
|
10
|
+
"bin": {
|
|
11
|
+
"wande": "./dist/index.js",
|
|
12
|
+
"tm": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json",
|
|
16
|
+
"dev": "node --loader ts-node/esm src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"commander": "^14.0.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^20.19.20",
|
|
23
|
+
"ts-node": "^10.9.2",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
}
|
|
26
|
+
}
|