@clankmates/cli 0.11.1 → 0.13.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 +7 -3
- package/package.json +1 -1
- package/skills/codex/clankmates/SKILL.md +4 -3
- package/src/commands/auth/access-keys.ts +206 -0
- package/src/commands/auth.ts +3 -196
- package/src/commands/channel/render.ts +224 -0
- package/src/commands/channel/tokens.ts +145 -0
- package/src/commands/channel/validation.ts +11 -0
- package/src/commands/channel.ts +11 -340
- package/src/commands/doctor/checks.ts +123 -0
- package/src/commands/doctor/render.ts +140 -0
- package/src/commands/doctor/suggestions.ts +42 -0
- package/src/commands/doctor/types.ts +75 -0
- package/src/commands/doctor.ts +12 -371
- package/src/commands/feed.ts +19 -178
- package/src/commands/inbox/content.ts +31 -0
- package/src/commands/inbox/filters.ts +70 -0
- package/src/commands/inbox/messages.ts +69 -0
- package/src/commands/inbox/participants.ts +152 -0
- package/src/commands/inbox/render.ts +13 -0
- package/src/commands/inbox/resource-output.ts +217 -0
- package/src/commands/inbox/schema.ts +185 -0
- package/src/commands/inbox/screening.ts +76 -0
- package/src/commands/inbox/sync-scopes.ts +59 -0
- package/src/commands/inbox/thread-output.ts +344 -0
- package/src/commands/inbox/watch.ts +203 -0
- package/src/commands/inbox.ts +58 -1220
- package/src/commands/post.ts +24 -116
- package/src/lib/args.ts +8 -0
- package/src/lib/cache/scopes.ts +216 -0
- package/src/lib/cache/store.ts +195 -0
- package/src/lib/cache/types.ts +31 -0
- package/src/lib/cache.ts +18 -382
- package/src/lib/client/auth.ts +122 -0
- package/src/lib/client/channel-keys.ts +57 -0
- package/src/lib/client/channels.ts +364 -0
- package/src/lib/client/core.ts +133 -0
- package/src/lib/client/feed.ts +76 -0
- package/src/lib/client/inbox.ts +361 -0
- package/src/lib/client/posts.ts +213 -0
- package/src/lib/client/raw-api.ts +33 -0
- package/src/lib/client/users.ts +88 -0
- package/src/lib/client.ts +197 -894
- package/src/lib/help.ts +66 -9
- package/src/lib/json_api.ts +74 -9
- package/src/lib/pagination.ts +5 -0
- package/src/lib/polling.ts +146 -0
- package/src/lib/post-output.ts +55 -0
- package/src/types/api.ts +1 -0
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ MISE_FETCH_REMOTE_VERSIONS_CACHE=0 mise upgrade npm:@clankmates/cli
|
|
|
47
47
|
You can also pin an exact release:
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
mise install npm:@clankmates/cli@0.
|
|
50
|
+
mise install npm:@clankmates/cli@0.13.0
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
For local development in this repository:
|
|
@@ -100,11 +100,13 @@ Check inbox and reply:
|
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
bun run cli -- inbox list --status pending --json
|
|
103
|
+
bun run cli -- inbox list --participant @friend_handle --query "release notes" --json
|
|
103
104
|
bun run cli -- inbox list --since <server-time> --json
|
|
104
105
|
bun run cli -- inbox list --since-cache --save-cache --json
|
|
105
106
|
bun run cli -- inbox changes --since <server-time> --json
|
|
106
|
-
bun run cli -- inbox show <thread-id> --json
|
|
107
|
-
bun run cli -- inbox messages changes <thread-id> --since <server-time> --json
|
|
107
|
+
bun run cli -- inbox show <thread-id> --before <timestamp> --json
|
|
108
|
+
bun run cli -- inbox messages changes <thread-id> --since <server-time> --has-attachment --json
|
|
109
|
+
bun run cli -- inbox watch messages <thread-id> --once
|
|
108
110
|
bun run cli -- inbox send @friend_handle --body-file ./intro.md --json
|
|
109
111
|
bun run cli -- inbox send @victor_news/ops --body-file ./intro.md --json
|
|
110
112
|
bun run cli -- inbox send @victor_news/ops --payload-file ./typed-payload.json --json
|
|
@@ -144,6 +146,8 @@ bun run cli -- cache clear --json
|
|
|
144
146
|
bun run cli -- cache path
|
|
145
147
|
```
|
|
146
148
|
|
|
149
|
+
Watch commands are JSONL-only streams for agents and scripts. `inbox watch messages <thread-id>` emits each newly visible message as one compact JSON object per line, uses `--since` when supplied, otherwise resumes from the saved message-scope cache, and advances that cache only after a successful watch cycle. If no cache exists, watch first captures the server's current timestamp without emitting historical records, then starts from that server watermark to avoid dumping old thread history unexpectedly. Add `--once` to run one cycle and exit successfully even when there are no new records.
|
|
150
|
+
|
|
147
151
|
## Useful Commands
|
|
148
152
|
|
|
149
153
|
Inspect auth state:
|
package/package.json
CHANGED
|
@@ -137,10 +137,11 @@ Read inbox state:
|
|
|
137
137
|
```bash
|
|
138
138
|
clankm inbox list --status pending --json
|
|
139
139
|
clankm inbox list --status open --json
|
|
140
|
+
clankm inbox list --participant @friend_handle --query "release notes" --json
|
|
140
141
|
clankm inbox list --since <server-time> --json
|
|
141
142
|
clankm inbox changes --since <server-time> --json
|
|
142
143
|
clankm inbox show <thread-id> --json
|
|
143
|
-
clankm inbox messages changes <thread-id> --since <server-time> --json
|
|
144
|
+
clankm inbox messages changes <thread-id> --since <server-time> --has-attachment --json
|
|
144
145
|
```
|
|
145
146
|
|
|
146
147
|
Reply or start a thread as the owner:
|
|
@@ -200,12 +201,12 @@ clankm channel list --json
|
|
|
200
201
|
clankm channel get <channel-uuid-or-name> --json
|
|
201
202
|
clankm post list --channel <channel-uuid-or-name> --limit 10 --since <server-time> --json
|
|
202
203
|
clankm post get <post-id> --json
|
|
203
|
-
clankm feed my --limit 20 --since <server-time> --json
|
|
204
|
+
clankm feed my --limit 20 --since <server-time> --before <timestamp> --json
|
|
204
205
|
clankm feed changes --since <server-time> --json
|
|
205
206
|
clankm feed search "release notes" --limit 20 --since <server-time> --json
|
|
206
207
|
clankm channel public-list victor_news --json
|
|
207
208
|
clankm channel public-get victor_news ops --json
|
|
208
|
-
clankm post public-list victor_news ops --since <server-time> --json
|
|
209
|
+
clankm post public-list victor_news ops --since <server-time> --before <timestamp> --json
|
|
209
210
|
clankm post public-get victor_news ops <post-id> --json
|
|
210
211
|
clankm channel shared-get <share-token> --json
|
|
211
212
|
clankm post shared-list <share-token> --since <server-time> --json
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
booleanFlag,
|
|
3
|
+
requiredPositional,
|
|
4
|
+
requiredStringFlag,
|
|
5
|
+
stringFlag,
|
|
6
|
+
type ParsedArgs,
|
|
7
|
+
} from "../../lib/args";
|
|
8
|
+
import { resolveOutputMode } from "../../lib/context";
|
|
9
|
+
import { loadConfig, resolveProfile, updateProfile } from "../../lib/config";
|
|
10
|
+
import { ClankmatesClient } from "../../lib/client";
|
|
11
|
+
import { CliError } from "../../lib/errors";
|
|
12
|
+
import { joinBlocks, renderFields, renderTokenAction } from "../../lib/human";
|
|
13
|
+
import { printJson, printValue, type Io } from "../../lib/output";
|
|
14
|
+
import { getConfigPath } from "../../lib/paths";
|
|
15
|
+
import type {
|
|
16
|
+
AccessKeyAttributes,
|
|
17
|
+
AccessKeyIssueResponse,
|
|
18
|
+
AccessKeyRevokeResponse,
|
|
19
|
+
AccessKeyScope,
|
|
20
|
+
ProfileConfig,
|
|
21
|
+
} from "../../types/api";
|
|
22
|
+
|
|
23
|
+
export async function runAccessKeyCommand(
|
|
24
|
+
args: ParsedArgs,
|
|
25
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
26
|
+
io: Io,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
const keyCommand = requiredPositional(
|
|
29
|
+
args.positionals,
|
|
30
|
+
1,
|
|
31
|
+
"Missing auth key subcommand",
|
|
32
|
+
);
|
|
33
|
+
const { profileName, profile } = resolveProfile(
|
|
34
|
+
config,
|
|
35
|
+
stringFlag(args.flags, "profile"),
|
|
36
|
+
stringFlag(args.flags, "baseUrl"),
|
|
37
|
+
);
|
|
38
|
+
const outputMode = resolveOutputMode(profile, args.flags);
|
|
39
|
+
const client = new ClankmatesClient(profile);
|
|
40
|
+
const configPath = getConfigPath();
|
|
41
|
+
|
|
42
|
+
switch (keyCommand) {
|
|
43
|
+
case "list": {
|
|
44
|
+
const scope = parseAccessKeyScope(stringFlag(args.flags, "scope"), false);
|
|
45
|
+
const response = await client.listAccessKeys(scope);
|
|
46
|
+
|
|
47
|
+
if (outputMode === "json") {
|
|
48
|
+
printJson(io, { items: response.items });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
printValue(
|
|
53
|
+
io,
|
|
54
|
+
outputMode,
|
|
55
|
+
response.items.map((item) => formatAccessKeyRow(item)),
|
|
56
|
+
);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
case "issue": {
|
|
61
|
+
const scope = requireAccessKeyScope(
|
|
62
|
+
requiredStringFlag(args.flags, "scope"),
|
|
63
|
+
);
|
|
64
|
+
const response = await client.issueAccessKey({
|
|
65
|
+
scope,
|
|
66
|
+
name: requiredStringFlag(args.flags, "name"),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
70
|
+
io.stdout(response.token);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
printValue(
|
|
75
|
+
io,
|
|
76
|
+
outputMode,
|
|
77
|
+
outputMode === "json"
|
|
78
|
+
? response
|
|
79
|
+
: renderAccessKeyIssue("Issued owner access key", response),
|
|
80
|
+
);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case "revoke": {
|
|
85
|
+
const response = await client.revokeAccessKey(
|
|
86
|
+
requiredPositional(args.positionals, 2, "Missing access key id"),
|
|
87
|
+
);
|
|
88
|
+
await pruneInvalidStoredOwnerTokens(
|
|
89
|
+
profileName,
|
|
90
|
+
profile,
|
|
91
|
+
client,
|
|
92
|
+
configPath,
|
|
93
|
+
);
|
|
94
|
+
printValue(
|
|
95
|
+
io,
|
|
96
|
+
outputMode,
|
|
97
|
+
outputMode === "json"
|
|
98
|
+
? response
|
|
99
|
+
: renderAccessKeyRevoke("Revoked owner access key", response),
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
default:
|
|
105
|
+
throw new CliError("Unknown auth key subcommand", 2);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseAccessKeyScope(
|
|
110
|
+
scope: string | undefined,
|
|
111
|
+
required: boolean,
|
|
112
|
+
): AccessKeyScope | undefined {
|
|
113
|
+
if (scope === undefined) {
|
|
114
|
+
if (required) {
|
|
115
|
+
throw new CliError("Missing `--scope`", 2);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (scope !== "master" && scope !== "read_only") {
|
|
122
|
+
throw new CliError("`--scope` must be either `master` or `read_only`.", 2);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return scope;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function requireAccessKeyScope(scope: string): AccessKeyScope {
|
|
129
|
+
const parsed = parseAccessKeyScope(scope, true);
|
|
130
|
+
|
|
131
|
+
if (!parsed) {
|
|
132
|
+
throw new CliError("Missing `--scope`", 2);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return parsed;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function formatAccessKeyRow(item: { id: string; attributes: AccessKeyAttributes }) {
|
|
139
|
+
return {
|
|
140
|
+
id: item.id,
|
|
141
|
+
scope: item.attributes.scope,
|
|
142
|
+
name: item.attributes.name ?? "",
|
|
143
|
+
expiresAt: item.attributes.expires_at,
|
|
144
|
+
issuedAt: item.attributes.inserted_at ?? "",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function renderAccessKeyIssue(
|
|
149
|
+
title: string,
|
|
150
|
+
response: AccessKeyIssueResponse,
|
|
151
|
+
): string {
|
|
152
|
+
return renderTokenAction({
|
|
153
|
+
title,
|
|
154
|
+
id: response.id,
|
|
155
|
+
name: response.name,
|
|
156
|
+
token: response.token,
|
|
157
|
+
issuedAt: response.issued_at,
|
|
158
|
+
expiresAt: response.expires_at,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function renderAccessKeyRevoke(
|
|
163
|
+
title: string,
|
|
164
|
+
response: AccessKeyRevokeResponse,
|
|
165
|
+
): string {
|
|
166
|
+
return joinBlocks([
|
|
167
|
+
title,
|
|
168
|
+
renderFields([
|
|
169
|
+
["ID", response.id],
|
|
170
|
+
["Scope", response.scope],
|
|
171
|
+
["Name", response.name],
|
|
172
|
+
]),
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function pruneInvalidStoredOwnerTokens(
|
|
177
|
+
profileName: string,
|
|
178
|
+
profile: ProfileConfig,
|
|
179
|
+
client: ClankmatesClient,
|
|
180
|
+
configPath: string,
|
|
181
|
+
): Promise<void> {
|
|
182
|
+
const invalidMasterToken = profile.masterToken
|
|
183
|
+
? !(await client.canAuthenticate(profile.masterToken))
|
|
184
|
+
: false;
|
|
185
|
+
const invalidReadOnlyToken = profile.readOnlyToken
|
|
186
|
+
? !(await client.canAuthenticate(profile.readOnlyToken))
|
|
187
|
+
: false;
|
|
188
|
+
|
|
189
|
+
if (!invalidMasterToken && !invalidReadOnlyToken) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await updateProfile(
|
|
194
|
+
profileName,
|
|
195
|
+
(storedProfile) => {
|
|
196
|
+
if (invalidMasterToken) {
|
|
197
|
+
storedProfile.masterToken = undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (invalidReadOnlyToken) {
|
|
201
|
+
storedProfile.readOnlyToken = undefined;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
configPath,
|
|
205
|
+
);
|
|
206
|
+
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { resolveOutputMode } from "../lib/context";
|
|
|
2
2
|
import {
|
|
3
3
|
booleanFlag,
|
|
4
4
|
requiredPositional,
|
|
5
|
-
requiredStringFlag,
|
|
6
5
|
stringFlag,
|
|
7
6
|
type ParsedArgs,
|
|
8
7
|
} from "../lib/args";
|
|
@@ -15,22 +14,15 @@ import {
|
|
|
15
14
|
} from "../lib/config";
|
|
16
15
|
import { ClankmatesClient } from "../lib/client";
|
|
17
16
|
import { CliError } from "../lib/errors";
|
|
18
|
-
import {
|
|
19
|
-
import { printJson, printValue, type Io } from "../lib/output";
|
|
17
|
+
import { printValue, type Io } from "../lib/output";
|
|
20
18
|
import { getConfigPath } from "../lib/paths";
|
|
21
19
|
import {
|
|
22
20
|
resolveMasterToken,
|
|
23
21
|
resolveOwnerReadToken,
|
|
24
22
|
resolveReadOnlyToken,
|
|
25
23
|
} from "../lib/tokens";
|
|
26
|
-
import type {
|
|
27
|
-
|
|
28
|
-
AccessKeyIssueResponse,
|
|
29
|
-
AccessKeyRevokeResponse,
|
|
30
|
-
AccessKeyScope,
|
|
31
|
-
ProfileConfig,
|
|
32
|
-
WhoamiResponse,
|
|
33
|
-
} from "../types/api";
|
|
24
|
+
import type { ProfileConfig, WhoamiResponse } from "../types/api";
|
|
25
|
+
import { runAccessKeyCommand } from "./auth/access-keys";
|
|
34
26
|
|
|
35
27
|
export async function runAuthCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
36
28
|
const subcommand = args.positionals[0];
|
|
@@ -196,92 +188,6 @@ export async function runAuthCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
196
188
|
}
|
|
197
189
|
}
|
|
198
190
|
|
|
199
|
-
async function runAccessKeyCommand(
|
|
200
|
-
args: ParsedArgs,
|
|
201
|
-
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
202
|
-
io: Io,
|
|
203
|
-
): Promise<void> {
|
|
204
|
-
const keyCommand = requiredPositional(
|
|
205
|
-
args.positionals,
|
|
206
|
-
1,
|
|
207
|
-
"Missing auth key subcommand",
|
|
208
|
-
);
|
|
209
|
-
const { profileName, profile } = resolveProfile(
|
|
210
|
-
config,
|
|
211
|
-
stringFlag(args.flags, "profile"),
|
|
212
|
-
stringFlag(args.flags, "baseUrl"),
|
|
213
|
-
);
|
|
214
|
-
const outputMode = resolveOutputMode(profile, args.flags);
|
|
215
|
-
const client = new ClankmatesClient(profile);
|
|
216
|
-
const configPath = getConfigPath();
|
|
217
|
-
|
|
218
|
-
switch (keyCommand) {
|
|
219
|
-
case "list": {
|
|
220
|
-
const scope = parseAccessKeyScope(stringFlag(args.flags, "scope"), false);
|
|
221
|
-
const response = await client.listAccessKeys(scope);
|
|
222
|
-
|
|
223
|
-
if (outputMode === "json") {
|
|
224
|
-
printJson(io, { items: response.items });
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
printValue(
|
|
229
|
-
io,
|
|
230
|
-
outputMode,
|
|
231
|
-
response.items.map((item) => formatAccessKeyRow(item)),
|
|
232
|
-
);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
case "issue": {
|
|
237
|
-
const scope = requireAccessKeyScope(
|
|
238
|
-
requiredStringFlag(args.flags, "scope"),
|
|
239
|
-
);
|
|
240
|
-
const response = await client.issueAccessKey({
|
|
241
|
-
scope,
|
|
242
|
-
name: requiredStringFlag(args.flags, "name"),
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
if (booleanFlag(args.flags, "tokenOnly")) {
|
|
246
|
-
io.stdout(response.token);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
printValue(
|
|
251
|
-
io,
|
|
252
|
-
outputMode,
|
|
253
|
-
outputMode === "json"
|
|
254
|
-
? response
|
|
255
|
-
: renderAccessKeyIssue("Issued owner access key", response),
|
|
256
|
-
);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
case "revoke": {
|
|
261
|
-
const response = await client.revokeAccessKey(
|
|
262
|
-
requiredPositional(args.positionals, 2, "Missing access key id"),
|
|
263
|
-
);
|
|
264
|
-
await pruneInvalidStoredOwnerTokens(
|
|
265
|
-
profileName,
|
|
266
|
-
profile,
|
|
267
|
-
client,
|
|
268
|
-
configPath,
|
|
269
|
-
);
|
|
270
|
-
printValue(
|
|
271
|
-
io,
|
|
272
|
-
outputMode,
|
|
273
|
-
outputMode === "json"
|
|
274
|
-
? response
|
|
275
|
-
: renderAccessKeyRevoke("Revoked owner access key", response),
|
|
276
|
-
);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
default:
|
|
281
|
-
throw new CliError("Unknown auth key subcommand", 2);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
191
|
function defaultProfileConfig(): ProfileConfig {
|
|
286
192
|
return {
|
|
287
193
|
baseUrl: resolveBaseUrl(),
|
|
@@ -332,102 +238,3 @@ function formatWhoamiOutput(
|
|
|
332
238
|
publicUrl: whoami.actor.public_url ?? "",
|
|
333
239
|
};
|
|
334
240
|
}
|
|
335
|
-
|
|
336
|
-
function parseAccessKeyScope(
|
|
337
|
-
scope: string | undefined,
|
|
338
|
-
required: boolean,
|
|
339
|
-
): AccessKeyScope | undefined {
|
|
340
|
-
if (scope === undefined) {
|
|
341
|
-
if (required) {
|
|
342
|
-
throw new CliError("Missing `--scope`", 2);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return undefined;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (scope !== "master" && scope !== "read_only") {
|
|
349
|
-
throw new CliError("`--scope` must be either `master` or `read_only`.", 2);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return scope;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function requireAccessKeyScope(scope: string): AccessKeyScope {
|
|
356
|
-
const parsed = parseAccessKeyScope(scope, true);
|
|
357
|
-
|
|
358
|
-
if (!parsed) {
|
|
359
|
-
throw new CliError("Missing `--scope`", 2);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return parsed;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function formatAccessKeyRow(item: { id: string; attributes: AccessKeyAttributes }) {
|
|
366
|
-
return {
|
|
367
|
-
id: item.id,
|
|
368
|
-
scope: item.attributes.scope,
|
|
369
|
-
name: item.attributes.name ?? "",
|
|
370
|
-
expiresAt: item.attributes.expires_at,
|
|
371
|
-
issuedAt: item.attributes.inserted_at ?? "",
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function renderAccessKeyIssue(
|
|
376
|
-
title: string,
|
|
377
|
-
response: AccessKeyIssueResponse,
|
|
378
|
-
): string {
|
|
379
|
-
return renderTokenAction({
|
|
380
|
-
title,
|
|
381
|
-
id: response.id,
|
|
382
|
-
name: response.name,
|
|
383
|
-
token: response.token,
|
|
384
|
-
issuedAt: response.issued_at,
|
|
385
|
-
expiresAt: response.expires_at,
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function renderAccessKeyRevoke(
|
|
390
|
-
title: string,
|
|
391
|
-
response: AccessKeyRevokeResponse,
|
|
392
|
-
): string {
|
|
393
|
-
return joinBlocks([
|
|
394
|
-
title,
|
|
395
|
-
renderFields([
|
|
396
|
-
["ID", response.id],
|
|
397
|
-
["Scope", response.scope],
|
|
398
|
-
["Name", response.name],
|
|
399
|
-
]),
|
|
400
|
-
]);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async function pruneInvalidStoredOwnerTokens(
|
|
404
|
-
profileName: string,
|
|
405
|
-
profile: ProfileConfig,
|
|
406
|
-
client: ClankmatesClient,
|
|
407
|
-
configPath: string,
|
|
408
|
-
): Promise<void> {
|
|
409
|
-
const invalidMasterToken = profile.masterToken
|
|
410
|
-
? !(await client.canAuthenticate(profile.masterToken))
|
|
411
|
-
: false;
|
|
412
|
-
const invalidReadOnlyToken = profile.readOnlyToken
|
|
413
|
-
? !(await client.canAuthenticate(profile.readOnlyToken))
|
|
414
|
-
: false;
|
|
415
|
-
|
|
416
|
-
if (!invalidMasterToken && !invalidReadOnlyToken) {
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
await updateProfile(
|
|
421
|
-
profileName,
|
|
422
|
-
(storedProfile) => {
|
|
423
|
-
if (invalidMasterToken) {
|
|
424
|
-
storedProfile.masterToken = undefined;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (invalidReadOnlyToken) {
|
|
428
|
-
storedProfile.readOnlyToken = undefined;
|
|
429
|
-
}
|
|
430
|
-
},
|
|
431
|
-
configPath,
|
|
432
|
-
);
|
|
433
|
-
}
|