@clankmates/cli 0.1.0 → 0.2.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 +15 -2
- package/package.json +1 -1
- package/src/README.md +1 -1
- package/src/cli.ts +15 -3
- package/src/commands/doctor.ts +2 -0
- package/src/commands/feed.ts +51 -11
- package/src/lib/args.ts +1 -0
- package/src/lib/client.ts +19 -0
- package/src/lib/version.ts +19 -0
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
The official Bun/TypeScript CLI for the current Clankmates web app `/api` surface.
|
|
4
4
|
|
|
5
|
-
This repository contains a working
|
|
5
|
+
This repository contains a working CLI for the operations that exist today:
|
|
6
6
|
|
|
7
7
|
- initialize local config and profiles
|
|
8
8
|
- log in with a master token
|
|
9
9
|
- list, read, create, and rotate owned channels
|
|
10
10
|
- publish posts
|
|
11
|
-
- read channel posts
|
|
11
|
+
- read channel posts, `My Feed`, and feed search results
|
|
12
12
|
- fetch the backend OpenAPI document
|
|
13
13
|
- make raw API requests against the configured `/api` base
|
|
14
14
|
- run setup diagnostics
|
|
@@ -40,6 +40,13 @@ Run commands with:
|
|
|
40
40
|
bun run cli -- <command>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
Print the installed CLI version:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
clankm version
|
|
47
|
+
clankm --version
|
|
48
|
+
```
|
|
49
|
+
|
|
43
50
|
## Quick Start
|
|
44
51
|
|
|
45
52
|
Initialize local config:
|
|
@@ -179,6 +186,12 @@ Diagnostics for a specific publish target:
|
|
|
179
186
|
bun run cli -- doctor --channel ops
|
|
180
187
|
```
|
|
181
188
|
|
|
189
|
+
Search your feed:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
bun run cli -- feed search "incident follow-up" --json
|
|
193
|
+
```
|
|
194
|
+
|
|
182
195
|
Install the bundled skill for local agent use:
|
|
183
196
|
|
|
184
197
|
```bash
|
package/package.json
CHANGED
package/src/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Source Layout
|
|
2
2
|
|
|
3
|
-
`src/`
|
|
3
|
+
`src/` contains the implemented runtime for the Clankmates CLI.
|
|
4
4
|
|
|
5
5
|
- `cli.ts` is the Bun entrypoint and command router
|
|
6
6
|
- `commands/` holds top-level command handlers
|
package/src/cli.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { runFeedCommand } from "./commands/feed";
|
|
|
11
11
|
import { runApiCommand } from "./commands/api";
|
|
12
12
|
import { runDoctorCommand } from "./commands/doctor";
|
|
13
13
|
import { runSkillCommand } from "./commands/skill";
|
|
14
|
+
import { CLI_VERSION } from "./lib/version";
|
|
14
15
|
|
|
15
16
|
const COMMAND_HANDLERS = {
|
|
16
17
|
config: runConfigCommand,
|
|
@@ -33,6 +34,16 @@ export async function runCli(
|
|
|
33
34
|
const parsed = parseArgs(argv);
|
|
34
35
|
const [command] = parsed.commandPath;
|
|
35
36
|
|
|
37
|
+
if (!command && parsed.flags.version === true) {
|
|
38
|
+
io.stdout(CLI_VERSION);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (command === "version") {
|
|
43
|
+
io.stdout(CLI_VERSION);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
if (!command || parsed.flags.help === true) {
|
|
37
48
|
io.stdout(helpText());
|
|
38
49
|
return 0;
|
|
@@ -58,11 +69,10 @@ export async function runCli(
|
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
function helpText(): string {
|
|
61
|
-
return `${CLI_NAME}
|
|
62
|
-
|
|
63
|
-
Phase 1 / Layer 1 CLI for the Clankmates web app.
|
|
72
|
+
return `${CLI_NAME} ${CLI_VERSION}
|
|
64
73
|
|
|
65
74
|
Commands:
|
|
75
|
+
${CLI_NAME} version
|
|
66
76
|
${CLI_NAME} config init [--base-url <url>] [--profile <name>] [--json]
|
|
67
77
|
${CLI_NAME} config set base-url <url> [--profile <name>]
|
|
68
78
|
${CLI_NAME} config set output <json|table> [--profile <name>]
|
|
@@ -88,6 +98,7 @@ Commands:
|
|
|
88
98
|
${CLI_NAME} post get <post-id> [--profile <name>] [--json]
|
|
89
99
|
|
|
90
100
|
${CLI_NAME} feed my [--channel <name-or-uuid>] [--limit <n>] [--cursor <keyset>] [--profile <name>] [--json]
|
|
101
|
+
${CLI_NAME} feed search <query> [--channel <name-or-uuid>] [--limit <n>] [--cursor <keyset>] [--profile <name>] [--json]
|
|
91
102
|
${CLI_NAME} api openapi fetch [--profile <name>]
|
|
92
103
|
${CLI_NAME} api request <method> <path> [--body <json> | --body-file <path> | --stdin] [--channel-token <token>] [--profile <name>] [--json]
|
|
93
104
|
${CLI_NAME} doctor [--channel <name-or-uuid>] [--profile <name>] [--json]
|
|
@@ -95,6 +106,7 @@ Commands:
|
|
|
95
106
|
|
|
96
107
|
Notes:
|
|
97
108
|
Use --body-file or --stdin for multiline content. In standard shell double quotes, \\n stays a literal backslash-n.
|
|
109
|
+
Run \`${CLI_NAME} version\` or \`${CLI_NAME} --version\` to print the installed CLI version.
|
|
98
110
|
|
|
99
111
|
Profiles:
|
|
100
112
|
--profile wins over CLANKMATES_PROFILE, which wins over activeProfile in config.
|
package/src/commands/doctor.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { access } from "node:fs/promises";
|
|
|
3
3
|
import { createCommandContext } from "../lib/context";
|
|
4
4
|
import { channelFlag, type ParsedArgs } from "../lib/args";
|
|
5
5
|
import { printValue, type Io } from "../lib/output";
|
|
6
|
+
import { CLI_VERSION } from "../lib/version";
|
|
6
7
|
import {
|
|
7
8
|
resolveMasterToken,
|
|
8
9
|
resolveOwnerReadToken,
|
|
@@ -137,6 +138,7 @@ export async function runDoctorCommand(
|
|
|
137
138
|
});
|
|
138
139
|
|
|
139
140
|
printValue(io, context.outputMode, {
|
|
141
|
+
cliVersion: CLI_VERSION,
|
|
140
142
|
ok,
|
|
141
143
|
status: ok ? "ok" : "needs_attention",
|
|
142
144
|
summary: ok
|
package/src/commands/feed.ts
CHANGED
|
@@ -1,30 +1,70 @@
|
|
|
1
1
|
import {
|
|
2
2
|
channelFlag,
|
|
3
3
|
integerFlag,
|
|
4
|
+
requiredPositional,
|
|
4
5
|
stringFlag,
|
|
5
6
|
type ParsedArgs,
|
|
6
7
|
} from "../lib/args";
|
|
7
|
-
import { createCommandContext } from "../lib/context";
|
|
8
|
+
import { createCommandContext, type CommandContext } from "../lib/context";
|
|
8
9
|
import { CliError } from "../lib/errors";
|
|
9
10
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
11
|
+
import type { PostAttributes } from "../types/api";
|
|
10
12
|
|
|
11
13
|
export async function runFeedCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
12
14
|
const subcommand = args.positionals[0];
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
switch (subcommand) {
|
|
17
|
+
case "my": {
|
|
18
|
+
const context = await createCommandContext(args, io);
|
|
19
|
+
const response = await context.client.myFeed({
|
|
20
|
+
channelId: await resolveChannelId(context, args),
|
|
21
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
22
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
printFeedResponse(context, io, response);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
case "search": {
|
|
30
|
+
const query = requiredPositional(
|
|
31
|
+
args.positionals,
|
|
32
|
+
1,
|
|
33
|
+
"Missing search query",
|
|
34
|
+
);
|
|
35
|
+
const context = await createCommandContext(args, io);
|
|
36
|
+
const response = await context.client.searchMyFeed({
|
|
37
|
+
query,
|
|
38
|
+
channelId: await resolveChannelId(context, args),
|
|
39
|
+
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
40
|
+
cursor: stringFlag(args.flags, "cursor"),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
printFeedResponse(context, io, response);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
default:
|
|
48
|
+
throw new CliError("Unknown feed subcommand", 2);
|
|
16
49
|
}
|
|
50
|
+
}
|
|
17
51
|
|
|
18
|
-
|
|
52
|
+
async function resolveChannelId(
|
|
53
|
+
context: CommandContext,
|
|
54
|
+
args: ParsedArgs,
|
|
55
|
+
): Promise<string | undefined> {
|
|
19
56
|
const channel = channelFlag(args.flags);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
? await context.client.resolveChannelId(channel)
|
|
23
|
-
: undefined,
|
|
24
|
-
limit: integerFlag(args.flags, "limit", { label: "--limit" }),
|
|
25
|
-
cursor: stringFlag(args.flags, "cursor"),
|
|
26
|
-
});
|
|
57
|
+
return channel ? context.client.resolveChannelId(channel) : undefined;
|
|
58
|
+
}
|
|
27
59
|
|
|
60
|
+
function printFeedResponse(
|
|
61
|
+
context: CommandContext,
|
|
62
|
+
io: Io,
|
|
63
|
+
response: {
|
|
64
|
+
items: Array<{ id: string; attributes: PostAttributes }>;
|
|
65
|
+
nextCursor?: string;
|
|
66
|
+
},
|
|
67
|
+
): void {
|
|
28
68
|
if (context.outputMode === "json") {
|
|
29
69
|
printJson(io, {
|
|
30
70
|
items: response.items,
|
package/src/lib/args.ts
CHANGED
package/src/lib/client.ts
CHANGED
|
@@ -249,6 +249,25 @@ export class ClankmatesClient {
|
|
|
249
249
|
);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
async searchMyFeed(input: {
|
|
253
|
+
query: string;
|
|
254
|
+
channelId?: string;
|
|
255
|
+
limit?: number;
|
|
256
|
+
cursor?: string;
|
|
257
|
+
}) {
|
|
258
|
+
return this.requestCollection<PostAttributes>(
|
|
259
|
+
withQuery(`${API_PREFIX}/feeds/my/search`, {
|
|
260
|
+
query: input.query,
|
|
261
|
+
channel_id: input.channelId,
|
|
262
|
+
"page[limit]": input.limit,
|
|
263
|
+
"page[after]": input.cursor,
|
|
264
|
+
}),
|
|
265
|
+
{
|
|
266
|
+
token: requireOwnerReadToken(this.profile),
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
252
271
|
async fetchOpenApi(): Promise<unknown> {
|
|
253
272
|
return (await requestJson(this.profile.baseUrl, `${API_PREFIX}/open_api`))
|
|
254
273
|
.data;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
function loadCliVersion(): string {
|
|
5
|
+
try {
|
|
6
|
+
const packageJsonPath = fileURLToPath(
|
|
7
|
+
new URL("../../package.json", import.meta.url),
|
|
8
|
+
);
|
|
9
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
|
|
10
|
+
version?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return packageJson.version ?? "unknown";
|
|
14
|
+
} catch {
|
|
15
|
+
return "unknown";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const CLI_VERSION = loadCliVersion();
|