@clankid/cli 0.17.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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +275 -0
- package/bin/clank +6 -0
- package/docs/usage/clankid-migration.md +59 -0
- package/package.json +47 -0
- package/skills/codex/clankid/SKILL.md +256 -0
- package/skills/codex/clankid/references/safety.md +28 -0
- package/skills/codex/clankid/references/setup.md +65 -0
- package/src/README.md +8 -0
- package/src/cli.ts +154 -0
- package/src/commands/.gitkeep +1 -0
- package/src/commands/account.ts +337 -0
- package/src/commands/api.ts +43 -0
- package/src/commands/auth/access-keys.ts +206 -0
- package/src/commands/auth.ts +240 -0
- package/src/commands/cache.ts +124 -0
- package/src/commands/config.ts +93 -0
- 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 +221 -0
- package/src/commands/feed.ts +185 -0
- package/src/commands/identity/keys.ts +145 -0
- package/src/commands/identity/render.ts +224 -0
- package/src/commands/identity/validation.ts +11 -0
- package/src/commands/identity.ts +295 -0
- package/src/commands/inbox/content.ts +13 -0
- package/src/commands/inbox/filters.ts +70 -0
- package/src/commands/inbox/messages.ts +71 -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 +62 -0
- package/src/commands/inbox/thread-output.ts +345 -0
- package/src/commands/inbox/watch.ts +207 -0
- package/src/commands/inbox.ts +374 -0
- package/src/commands/post.ts +406 -0
- package/src/commands/setup.ts +228 -0
- package/src/commands/skill.ts +41 -0
- package/src/commands/user.ts +33 -0
- package/src/lib/.gitkeep +1 -0
- package/src/lib/args.ts +231 -0
- package/src/lib/body-input.ts +55 -0
- package/src/lib/cache/scopes.ts +272 -0
- package/src/lib/cache/store.ts +195 -0
- package/src/lib/cache/types.ts +31 -0
- package/src/lib/cache.ts +135 -0
- package/src/lib/client/auth.ts +163 -0
- package/src/lib/client/core.ts +133 -0
- package/src/lib/client/feed.ts +93 -0
- package/src/lib/client/identities.ts +364 -0
- package/src/lib/client/identity-keys.ts +57 -0
- package/src/lib/client/inbox.ts +385 -0
- package/src/lib/client/posts.ts +236 -0
- package/src/lib/client/raw-api.ts +33 -0
- package/src/lib/client/users.ts +88 -0
- package/src/lib/client.ts +469 -0
- package/src/lib/config.ts +239 -0
- package/src/lib/content.ts +149 -0
- package/src/lib/context.ts +43 -0
- package/src/lib/errors.ts +17 -0
- package/src/lib/help.ts +1587 -0
- package/src/lib/http.ts +138 -0
- package/src/lib/human.ts +143 -0
- package/src/lib/json-input.ts +73 -0
- package/src/lib/json_api.ts +120 -0
- package/src/lib/output.ts +203 -0
- package/src/lib/pagination.ts +116 -0
- package/src/lib/paths.ts +44 -0
- package/src/lib/polling.ts +146 -0
- package/src/lib/post-output.ts +60 -0
- package/src/lib/skills.ts +137 -0
- package/src/lib/tokens.ts +284 -0
- package/src/lib/version.ts +19 -0
- package/src/types/.gitkeep +1 -0
- package/src/types/api.ts +320 -0
- package/src/types/placeholder.d.ts +1 -0
package/src/lib/args.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { parseArgs as parseNodeArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
import { CliError } from "./errors";
|
|
4
|
+
|
|
5
|
+
const CLI_OPTIONS = {
|
|
6
|
+
help: { type: "boolean", short: "h" },
|
|
7
|
+
version: { type: "boolean" },
|
|
8
|
+
json: { type: "boolean" },
|
|
9
|
+
profile: { type: "string" },
|
|
10
|
+
baseUrl: { type: "string" },
|
|
11
|
+
"base-url": { type: "string" },
|
|
12
|
+
output: { type: "string" },
|
|
13
|
+
host: { type: "string" },
|
|
14
|
+
masterToken: { type: "string" },
|
|
15
|
+
"master-token": { type: "string" },
|
|
16
|
+
readOnlyToken: { type: "string" },
|
|
17
|
+
"read-only-token": { type: "string" },
|
|
18
|
+
scope: { type: "string" },
|
|
19
|
+
name: { type: "string" },
|
|
20
|
+
description: { type: "string" },
|
|
21
|
+
email: { type: "string" },
|
|
22
|
+
handle: { type: "string" },
|
|
23
|
+
code: { type: "string" },
|
|
24
|
+
keyName: { type: "string" },
|
|
25
|
+
"key-name": { type: "string" },
|
|
26
|
+
bootstrapId: { type: "string" },
|
|
27
|
+
"bootstrap-id": { type: "string" },
|
|
28
|
+
localBypass: { type: "boolean" },
|
|
29
|
+
"local-bypass": { type: "boolean" },
|
|
30
|
+
save: { type: "boolean" },
|
|
31
|
+
force: { type: "boolean" },
|
|
32
|
+
copy: { type: "boolean" },
|
|
33
|
+
tokenOnly: { type: "boolean" },
|
|
34
|
+
"token-only": { type: "boolean" },
|
|
35
|
+
masterTokenStdin: { type: "boolean" },
|
|
36
|
+
"master-token-stdin": { type: "boolean" },
|
|
37
|
+
identity: { type: "string" },
|
|
38
|
+
"identity-id": { type: "string" },
|
|
39
|
+
from: { type: "string" },
|
|
40
|
+
identityKey: { type: "string" },
|
|
41
|
+
"identity-key": { type: "string" },
|
|
42
|
+
contextPostId: { type: "string" },
|
|
43
|
+
"context-post-id": { type: "string" },
|
|
44
|
+
body: { type: "string" },
|
|
45
|
+
bodyFile: { type: "string" },
|
|
46
|
+
"body-file": { type: "string" },
|
|
47
|
+
stdin: { type: "boolean" },
|
|
48
|
+
content: { type: "string" },
|
|
49
|
+
payload: { type: "string" },
|
|
50
|
+
payloadFile: { type: "string" },
|
|
51
|
+
"payload-file": { type: "string" },
|
|
52
|
+
payloadStdin: { type: "boolean" },
|
|
53
|
+
"payload-stdin": { type: "boolean" },
|
|
54
|
+
payloadContains: { type: "string" },
|
|
55
|
+
"payload-contains": { type: "string" },
|
|
56
|
+
payloadPath: { type: "string" },
|
|
57
|
+
"payload-path": { type: "string" },
|
|
58
|
+
payloadOp: { type: "string" },
|
|
59
|
+
"payload-op": { type: "string" },
|
|
60
|
+
payloadValue: { type: "string" },
|
|
61
|
+
"payload-value": { type: "string" },
|
|
62
|
+
schema: { type: "string" },
|
|
63
|
+
schemaFile: { type: "string" },
|
|
64
|
+
"schema-file": { type: "string" },
|
|
65
|
+
schemaStdin: { type: "boolean" },
|
|
66
|
+
"schema-stdin": { type: "boolean" },
|
|
67
|
+
limit: { type: "string" },
|
|
68
|
+
cursor: { type: "string" },
|
|
69
|
+
before: { type: "string" },
|
|
70
|
+
order: { type: "string" },
|
|
71
|
+
since: { type: "string" },
|
|
72
|
+
sinceCache: { type: "boolean" },
|
|
73
|
+
"since-cache": { type: "boolean" },
|
|
74
|
+
saveCache: { type: "boolean" },
|
|
75
|
+
"save-cache": { type: "boolean" },
|
|
76
|
+
once: { type: "boolean" },
|
|
77
|
+
status: { type: "string" },
|
|
78
|
+
mailbox: { type: "string" },
|
|
79
|
+
participant: { type: "string" },
|
|
80
|
+
participantScope: { type: "string" },
|
|
81
|
+
"participant-scope": { type: "string" },
|
|
82
|
+
query: { type: "string" },
|
|
83
|
+
hasAttachment: { type: "boolean" },
|
|
84
|
+
"has-attachment": { type: "boolean" },
|
|
85
|
+
} as const;
|
|
86
|
+
|
|
87
|
+
type ParsedValue = string | boolean;
|
|
88
|
+
|
|
89
|
+
const REMOVED_OPTIONS: Record<string, string> = {
|
|
90
|
+
"--sender-identity": "`--sender-identity` has been removed. Use `--from` instead.",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export interface ParsedArgs {
|
|
94
|
+
commandPath: string[];
|
|
95
|
+
positionals: string[];
|
|
96
|
+
flags: Record<string, ParsedValue>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function parseArgs(argv: string[]): ParsedArgs {
|
|
100
|
+
rejectRemovedOptions(argv);
|
|
101
|
+
|
|
102
|
+
const parsed = parseNodeArgs({
|
|
103
|
+
args: argv,
|
|
104
|
+
allowPositionals: true,
|
|
105
|
+
strict: false,
|
|
106
|
+
options: CLI_OPTIONS,
|
|
107
|
+
});
|
|
108
|
+
const [command, ...positionals] = parsed.positionals;
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
commandPath: command ? [command] : [],
|
|
112
|
+
positionals,
|
|
113
|
+
flags: normalizeFlags(
|
|
114
|
+
parsed.values as Record<string, ParsedValue | undefined>,
|
|
115
|
+
),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function rejectRemovedOptions(argv: string[]): void {
|
|
120
|
+
for (const arg of argv) {
|
|
121
|
+
const option = arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg;
|
|
122
|
+
const message = REMOVED_OPTIONS[option];
|
|
123
|
+
|
|
124
|
+
if (message) {
|
|
125
|
+
throw new CliError(message, 2);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function stringFlag(
|
|
131
|
+
flags: ParsedArgs["flags"],
|
|
132
|
+
key: string,
|
|
133
|
+
): string | undefined {
|
|
134
|
+
const value = flags[key];
|
|
135
|
+
return typeof value === "string" ? value : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function requiredStringFlag(
|
|
139
|
+
flags: ParsedArgs["flags"],
|
|
140
|
+
key: string,
|
|
141
|
+
): string {
|
|
142
|
+
const value = stringFlag(flags, key);
|
|
143
|
+
|
|
144
|
+
if (!value) {
|
|
145
|
+
throw new CliError(`Missing \`${toFlagName(key)}\``, 2);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function identityFlag(flags: ParsedArgs["flags"]): string | undefined {
|
|
152
|
+
return stringFlag(flags, "identity") ?? stringFlag(flags, "identityId");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function requiredIdentityFlag(flags: ParsedArgs["flags"]): string {
|
|
156
|
+
const value = identityFlag(flags);
|
|
157
|
+
|
|
158
|
+
if (!value) {
|
|
159
|
+
throw new CliError("Missing `--identity`", 2);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function integerFlag(
|
|
166
|
+
flags: ParsedArgs["flags"],
|
|
167
|
+
key: string,
|
|
168
|
+
options: { min?: number; label?: string } = {},
|
|
169
|
+
): number | undefined {
|
|
170
|
+
const value = stringFlag(flags, key);
|
|
171
|
+
|
|
172
|
+
if (!value) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const parsed = Number.parseInt(value, 10);
|
|
177
|
+
const minimum = options.min ?? 1;
|
|
178
|
+
|
|
179
|
+
if (!Number.isFinite(parsed) || parsed < minimum) {
|
|
180
|
+
throw new CliError(
|
|
181
|
+
`${options.label ?? toFlagName(key)} must be an integer >= ${minimum}`,
|
|
182
|
+
2,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return parsed;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function booleanFlag(flags: ParsedArgs["flags"], key: string): boolean {
|
|
190
|
+
return flags[key] === true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function requiredPositional(
|
|
194
|
+
positionals: string[],
|
|
195
|
+
index: number,
|
|
196
|
+
message: string,
|
|
197
|
+
): string {
|
|
198
|
+
const value = positionals[index];
|
|
199
|
+
|
|
200
|
+
if (!value) {
|
|
201
|
+
throw new CliError(message, 2);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function normalizeFlags(
|
|
208
|
+
values: Record<string, ParsedValue | undefined>,
|
|
209
|
+
): Record<string, ParsedValue> {
|
|
210
|
+
const flags: Record<string, ParsedValue> = {};
|
|
211
|
+
|
|
212
|
+
for (const [key, value] of Object.entries(values)) {
|
|
213
|
+
if (value === undefined) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
flags[toCamelCase(key)] = value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return flags;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function toCamelCase(value: string): string {
|
|
224
|
+
return value.replace(/-([a-z])/g, (_, letter: string) =>
|
|
225
|
+
letter.toUpperCase(),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function toFlagName(value: string): string {
|
|
230
|
+
return `--${value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
|
|
231
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { booleanFlag, stringFlag, type ParsedArgs } from "./args";
|
|
4
|
+
import { CliError } from "./errors";
|
|
5
|
+
|
|
6
|
+
export interface ResolveBodyInputOptions {
|
|
7
|
+
flags: ParsedArgs["flags"];
|
|
8
|
+
requireBody?: boolean;
|
|
9
|
+
readFileText?: (path: string) => Promise<string>;
|
|
10
|
+
readStdinText?: () => Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function resolveBodyInput({
|
|
14
|
+
flags,
|
|
15
|
+
requireBody = false,
|
|
16
|
+
readFileText = (path) => readFile(path, "utf8"),
|
|
17
|
+
readStdinText = () => readStdin()
|
|
18
|
+
}: ResolveBodyInputOptions): Promise<string | undefined> {
|
|
19
|
+
const inlineBody = stringFlag(flags, "body");
|
|
20
|
+
const bodyFile = stringFlag(flags, "bodyFile");
|
|
21
|
+
const useStdin = booleanFlag(flags, "stdin");
|
|
22
|
+
const providedCount = [inlineBody !== undefined, bodyFile !== undefined, useStdin].filter(Boolean).length;
|
|
23
|
+
|
|
24
|
+
if (requireBody && providedCount === 0) {
|
|
25
|
+
throw new CliError("Provide exactly one of `--body`, `--body-file`, or `--stdin`", 2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (providedCount > 1) {
|
|
29
|
+
throw new CliError("Use only one of `--body`, `--body-file`, or `--stdin`", 2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (inlineBody !== undefined) {
|
|
33
|
+
return inlineBody;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (bodyFile !== undefined) {
|
|
37
|
+
return readFileText(bodyFile);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (useStdin) {
|
|
41
|
+
return readStdinText();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function readStdin(stdin: AsyncIterable<unknown> = process.stdin): Promise<string> {
|
|
48
|
+
let buffer = "";
|
|
49
|
+
|
|
50
|
+
for await (const chunk of stdin) {
|
|
51
|
+
buffer += typeof chunk === "string" ? chunk : Buffer.from(chunk as ArrayBufferLike).toString("utf8");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return buffer;
|
|
55
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type { CommandContext } from "../context";
|
|
4
|
+
import type { PayloadFilters, WhoamiActor } from "../../types/api";
|
|
5
|
+
import type { CacheScope } from "./types";
|
|
6
|
+
|
|
7
|
+
const PUBLIC_ACTOR_KEY = "public";
|
|
8
|
+
const SHARED_ACTOR_KEY = "shared";
|
|
9
|
+
|
|
10
|
+
export function actorKey(actor: WhoamiActor): string {
|
|
11
|
+
if (actor.type === "user") {
|
|
12
|
+
return `user:${actor.id}:${actor.scope ?? "master"}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return `identity:${actor.id}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function publicActorKey(): string {
|
|
19
|
+
return PUBLIC_ACTOR_KEY;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function sharedActorKey(): string {
|
|
23
|
+
return SHARED_ACTOR_KEY;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function feedMyScope(input: {
|
|
27
|
+
context: CommandContext;
|
|
28
|
+
actorKey: string;
|
|
29
|
+
identityId?: string;
|
|
30
|
+
before?: string;
|
|
31
|
+
} & PayloadFilters): CacheScope {
|
|
32
|
+
const identity = input.identityId ?? "all";
|
|
33
|
+
const before = input.before ?? "default";
|
|
34
|
+
const payload = payloadFilterParts(input);
|
|
35
|
+
return scoped({
|
|
36
|
+
context: input.context,
|
|
37
|
+
actorKey: input.actorKey,
|
|
38
|
+
resource: "feed:my",
|
|
39
|
+
parts: ["feed", "my", identity, before, ...payload.parts],
|
|
40
|
+
params: { identity, before, ...payload.params },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function feedSearchScope(input: {
|
|
45
|
+
context: CommandContext;
|
|
46
|
+
actorKey: string;
|
|
47
|
+
query: string;
|
|
48
|
+
identityId?: string;
|
|
49
|
+
before?: string;
|
|
50
|
+
} & PayloadFilters): CacheScope {
|
|
51
|
+
const identity = input.identityId ?? "all";
|
|
52
|
+
const before = input.before ?? "default";
|
|
53
|
+
const payload = payloadFilterParts(input);
|
|
54
|
+
return scoped({
|
|
55
|
+
context: input.context,
|
|
56
|
+
actorKey: input.actorKey,
|
|
57
|
+
resource: "feed:search",
|
|
58
|
+
parts: ["feed", "search", input.query, identity, before, ...payload.parts],
|
|
59
|
+
params: { query: input.query, identity, before, ...payload.params },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function inboxThreadsScope(input: {
|
|
64
|
+
context: CommandContext;
|
|
65
|
+
actorKey: string;
|
|
66
|
+
status?: string;
|
|
67
|
+
mailbox?: string;
|
|
68
|
+
participant?: string;
|
|
69
|
+
participantScope?: string;
|
|
70
|
+
query?: string;
|
|
71
|
+
hasAttachment?: boolean;
|
|
72
|
+
before?: string;
|
|
73
|
+
} & PayloadFilters): CacheScope {
|
|
74
|
+
const status = input.status ?? "default";
|
|
75
|
+
const mailbox = input.mailbox ?? "default";
|
|
76
|
+
const participant = input.participant ?? "default";
|
|
77
|
+
const participantScope = input.participantScope ?? "default";
|
|
78
|
+
const query = input.query ?? "default";
|
|
79
|
+
const hasAttachment = input.hasAttachment ?? false;
|
|
80
|
+
const before = input.before ?? "default";
|
|
81
|
+
const payload = payloadFilterParts(input);
|
|
82
|
+
return scoped({
|
|
83
|
+
context: input.context,
|
|
84
|
+
actorKey: input.actorKey,
|
|
85
|
+
resource: "inbox:threads",
|
|
86
|
+
parts: [
|
|
87
|
+
"inbox",
|
|
88
|
+
"threads",
|
|
89
|
+
status,
|
|
90
|
+
mailbox,
|
|
91
|
+
participant,
|
|
92
|
+
participantScope,
|
|
93
|
+
query,
|
|
94
|
+
String(hasAttachment),
|
|
95
|
+
before,
|
|
96
|
+
...payload.parts,
|
|
97
|
+
input.actorKey,
|
|
98
|
+
],
|
|
99
|
+
params: {
|
|
100
|
+
status,
|
|
101
|
+
mailbox,
|
|
102
|
+
participant,
|
|
103
|
+
participantScope,
|
|
104
|
+
query,
|
|
105
|
+
hasAttachment,
|
|
106
|
+
before,
|
|
107
|
+
...payload.params,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function inboxMessagesScope(input: {
|
|
113
|
+
context: CommandContext;
|
|
114
|
+
actorKey: string;
|
|
115
|
+
threadId: string;
|
|
116
|
+
query?: string;
|
|
117
|
+
hasAttachment?: boolean;
|
|
118
|
+
before?: string;
|
|
119
|
+
} & PayloadFilters): CacheScope {
|
|
120
|
+
const query = input.query ?? "default";
|
|
121
|
+
const hasAttachment = input.hasAttachment ?? false;
|
|
122
|
+
const before = input.before ?? "default";
|
|
123
|
+
const payload = payloadFilterParts(input);
|
|
124
|
+
return scoped({
|
|
125
|
+
context: input.context,
|
|
126
|
+
actorKey: input.actorKey,
|
|
127
|
+
resource: "inbox:messages",
|
|
128
|
+
parts: [
|
|
129
|
+
"inbox",
|
|
130
|
+
"messages",
|
|
131
|
+
input.threadId,
|
|
132
|
+
query,
|
|
133
|
+
String(hasAttachment),
|
|
134
|
+
before,
|
|
135
|
+
...payload.parts,
|
|
136
|
+
input.actorKey,
|
|
137
|
+
],
|
|
138
|
+
params: {
|
|
139
|
+
threadId: input.threadId,
|
|
140
|
+
query,
|
|
141
|
+
hasAttachment,
|
|
142
|
+
before,
|
|
143
|
+
...payload.params,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function ownedPostsScope(input: {
|
|
149
|
+
context: CommandContext;
|
|
150
|
+
actorKey: string;
|
|
151
|
+
identityId: string;
|
|
152
|
+
before?: string;
|
|
153
|
+
} & PayloadFilters): CacheScope {
|
|
154
|
+
const before = input.before ?? "default";
|
|
155
|
+
const payload = payloadFilterParts(input);
|
|
156
|
+
return scoped({
|
|
157
|
+
context: input.context,
|
|
158
|
+
actorKey: input.actorKey,
|
|
159
|
+
resource: "posts:owned",
|
|
160
|
+
parts: ["posts", "owned", input.identityId, before, ...payload.parts],
|
|
161
|
+
params: { identityId: input.identityId, before, ...payload.params },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function publicPostsScope(input: {
|
|
166
|
+
context: CommandContext;
|
|
167
|
+
publicHandle: string;
|
|
168
|
+
identityName: string;
|
|
169
|
+
before?: string;
|
|
170
|
+
} & PayloadFilters): CacheScope {
|
|
171
|
+
const before = input.before ?? "default";
|
|
172
|
+
const payload = payloadFilterParts(input);
|
|
173
|
+
return scoped({
|
|
174
|
+
context: input.context,
|
|
175
|
+
actorKey: PUBLIC_ACTOR_KEY,
|
|
176
|
+
resource: "posts:public",
|
|
177
|
+
parts: [
|
|
178
|
+
"posts",
|
|
179
|
+
"public",
|
|
180
|
+
input.publicHandle,
|
|
181
|
+
input.identityName,
|
|
182
|
+
before,
|
|
183
|
+
...payload.parts,
|
|
184
|
+
],
|
|
185
|
+
params: {
|
|
186
|
+
publicHandle: input.publicHandle,
|
|
187
|
+
identityName: input.identityName,
|
|
188
|
+
before,
|
|
189
|
+
...payload.params,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function sharedPostsScope(input: {
|
|
195
|
+
context: CommandContext;
|
|
196
|
+
shareToken: string;
|
|
197
|
+
before?: string;
|
|
198
|
+
} & PayloadFilters): CacheScope {
|
|
199
|
+
const shareTokenHash = hashValue(input.shareToken);
|
|
200
|
+
const before = input.before ?? "default";
|
|
201
|
+
const payload = payloadFilterParts(input);
|
|
202
|
+
return scoped({
|
|
203
|
+
context: input.context,
|
|
204
|
+
actorKey: SHARED_ACTOR_KEY,
|
|
205
|
+
resource: "posts:shared",
|
|
206
|
+
parts: ["posts", "shared", shareTokenHash, before, ...payload.parts],
|
|
207
|
+
params: { shareTokenHash, before, ...payload.params },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function hashValue(value: string): string {
|
|
212
|
+
return createHash("sha256").update(value).digest("hex");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function scoped(input: {
|
|
216
|
+
context: CommandContext;
|
|
217
|
+
actorKey: string;
|
|
218
|
+
resource: string;
|
|
219
|
+
parts: string[];
|
|
220
|
+
params: Record<string, unknown>;
|
|
221
|
+
}): CacheScope {
|
|
222
|
+
const prefix = [
|
|
223
|
+
"v1",
|
|
224
|
+
normalizePart(input.context.profile.baseUrl),
|
|
225
|
+
normalizePart(input.context.profileName),
|
|
226
|
+
normalizePart(input.actorKey),
|
|
227
|
+
];
|
|
228
|
+
const scopeKey = [...prefix, ...input.parts.map(normalizePart)].join(":");
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
scopeKey,
|
|
232
|
+
resource: input.resource,
|
|
233
|
+
params: input.params,
|
|
234
|
+
actorKey: input.actorKey,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function normalizePart(value: string): string {
|
|
239
|
+
return encodeURIComponent(value);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function payloadFilterParts(input: PayloadFilters): {
|
|
243
|
+
parts: string[];
|
|
244
|
+
params: Record<string, unknown>;
|
|
245
|
+
} {
|
|
246
|
+
if (
|
|
247
|
+
input.contentFormat === undefined &&
|
|
248
|
+
input.payloadContains === undefined &&
|
|
249
|
+
input.payloadPath === undefined &&
|
|
250
|
+
input.payloadOp === undefined &&
|
|
251
|
+
input.payloadValue === undefined
|
|
252
|
+
) {
|
|
253
|
+
return { parts: [], params: {} };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const content = input.contentFormat ?? "default";
|
|
257
|
+
const contains = input.payloadContains ?? "default";
|
|
258
|
+
const path = input.payloadPath ?? "default";
|
|
259
|
+
const op = input.payloadOp ?? "default";
|
|
260
|
+
const value = input.payloadValue ?? "default";
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
parts: [content, contains, path, op, value],
|
|
264
|
+
params: {
|
|
265
|
+
content,
|
|
266
|
+
payloadContains: contains,
|
|
267
|
+
payloadPath: path,
|
|
268
|
+
payloadOp: op,
|
|
269
|
+
payloadValue: value,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|