@whooshbang/sdk 1.0.0-rc.17 → 1.0.0-rc.18

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.
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A very small argv reader.
3
+ *
4
+ * `node:util`'s `parseArgs` is here and capable, but it wants the option
5
+ * vocabulary declared up front and this CLI's options differ per command. The
6
+ * whole grammar is `wb <words…> [--flag value] [--switch]`, which is a dozen
7
+ * lines to read directly and no dependency to carry into a package a library
8
+ * consumer also installs.
9
+ */
10
+ export interface ParsedArguments {
11
+ /** The command path: `["project", "create"]`. */
12
+ readonly words: readonly string[];
13
+ readonly options: ReadonlyMap<string, string>;
14
+ readonly switches: ReadonlySet<string>;
15
+ /** Values repeated across several `--flag value` pairs, e.g. `--scope`. */
16
+ readonly repeated: ReadonlyMap<string, readonly string[]>;
17
+ }
18
+ export declare function parseArguments(argv: readonly string[]): ParsedArguments;
19
+ export declare class UsageError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ export declare function required(parsed: ParsedArguments, name: string, what: string): string;
@@ -0,0 +1,59 @@
1
+ const SWITCHES = new Set([
2
+ "help",
3
+ "json",
4
+ "version",
5
+ "credential-stdin",
6
+ "yes",
7
+ ]);
8
+ export function parseArguments(argv) {
9
+ const words = [];
10
+ const options = new Map();
11
+ const switches = new Set();
12
+ const repeated = new Map();
13
+ for (let index = 0; index < argv.length; index += 1) {
14
+ const token = argv[index];
15
+ if (!token.startsWith("--")) {
16
+ words.push(token);
17
+ continue;
18
+ }
19
+ const body = token.slice(2);
20
+ const equals = body.indexOf("=");
21
+ const name = equals === -1 ? body : body.slice(0, equals);
22
+ if (equals !== -1) {
23
+ const value = body.slice(equals + 1);
24
+ options.set(name, value);
25
+ repeated.set(name, [...(repeated.get(name) ?? []), value]);
26
+ continue;
27
+ }
28
+ if (SWITCHES.has(name)) {
29
+ switches.add(name);
30
+ continue;
31
+ }
32
+ const next = argv[index + 1];
33
+ if (next === undefined || next.startsWith("--")) {
34
+ // A bare unknown flag is a switch rather than an error: the command
35
+ // decides what it does and does not understand, and it can say so in its
36
+ // own words.
37
+ switches.add(name);
38
+ continue;
39
+ }
40
+ options.set(name, next);
41
+ repeated.set(name, [...(repeated.get(name) ?? []), next]);
42
+ index += 1;
43
+ }
44
+ return { options, repeated, switches, words };
45
+ }
46
+ export class UsageError extends Error {
47
+ constructor(message) {
48
+ super(message);
49
+ this.name = "UsageError";
50
+ }
51
+ }
52
+ export function required(parsed, name, what) {
53
+ const value = parsed.options.get(name);
54
+ if (value === undefined || value.length === 0) {
55
+ throw new UsageError(`--${name} is required: ${what}.`);
56
+ }
57
+ return value;
58
+ }
59
+ //# sourceMappingURL=arguments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arguments.js","sourceRoot":"","sources":["../../src/cli/arguments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ParsedArguments } from "./arguments.js";
2
+ import { type Writer } from "./output.js";
3
+ import { resolveOrigin } from "./session.js";
4
+ export interface CommandContext {
5
+ readonly parsed: ParsedArguments;
6
+ readonly writer: Writer;
7
+ readonly origin: string;
8
+ readonly openUrl: (url: string) => Promise<void>;
9
+ readonly readStdin: () => Promise<string>;
10
+ readonly credentialsFile?: string;
11
+ }
12
+ export declare function resolveCommand(words: readonly string[]): ((context: CommandContext) => Promise<void>) | null;
13
+ export declare const COMMAND_NAMES: string[];
14
+ export { resolveOrigin };
@@ -0,0 +1,257 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { required, UsageError } from "./arguments.js";
3
+ import { emit, emitSecret, table } from "./output.js";
4
+ import { authorizedClient, resolveOrigin, signIn, signOut } from "./session.js";
5
+ /**
6
+ * Every side-effecting call needs an idempotency key, and a person running a
7
+ * command by hand has no opinion about one. Generating it means a retried
8
+ * `wb message send` after a dropped connection is a replay rather than a
9
+ * second message — but only within the one invocation, which is why `--key`
10
+ * exists for anybody scripting a genuinely repeatable operation.
11
+ */
12
+ function idempotency(parsed) {
13
+ return { idempotencyKey: parsed.options.get("key") ?? randomUUID() };
14
+ }
15
+ /**
16
+ * `connection` unless asked otherwise, which matches what the API does for an
17
+ * ordinary subscriber id. Naming it on every send keeps a script auditable:
18
+ * somebody reading it can see which transport it meant without knowing the
19
+ * defaulting rule.
20
+ */
21
+ function deliveryTarget(parsed) {
22
+ const value = parsed.options.get("target") ?? "connection";
23
+ if (value !== "connection" && value !== "simulator") {
24
+ throw new UsageError("--target must be connection or simulator.");
25
+ }
26
+ return value;
27
+ }
28
+ function environmentArguments(parsed) {
29
+ return {
30
+ environmentId: required(parsed, "environment", "the environment id the operation acts in"),
31
+ projectId: required(parsed, "project", "the project id"),
32
+ };
33
+ }
34
+ async function client(context) {
35
+ return authorizedClient({
36
+ ...(context.credentialsFile === undefined
37
+ ? {}
38
+ : { credentialsFile: context.credentialsFile }),
39
+ origin: context.origin,
40
+ });
41
+ }
42
+ const commands = {
43
+ async login(context) {
44
+ const scopes = context.parsed.repeated.get("scope");
45
+ const session = await signIn({
46
+ ...(context.credentialsFile === undefined
47
+ ? {}
48
+ : { credentialsFile: context.credentialsFile }),
49
+ openUrl: context.openUrl,
50
+ origin: context.origin,
51
+ ...(scopes && scopes.length > 0 ? { scopes } : {}),
52
+ });
53
+ const api = await client(context);
54
+ const account = await api.getAccount();
55
+ emit(context.writer, { account: account.id, origin: context.origin, scopes: session.scopes }, [
56
+ `Signed in to ${context.origin}.`,
57
+ `Account ${account.id}.`,
58
+ `Permissions: ${session.scopes.join(", ")}`,
59
+ ]);
60
+ },
61
+ async logout(context) {
62
+ const removed = await signOut(context.origin, context.credentialsFile);
63
+ emit(context.writer, { origin: context.origin, signedOut: removed }, [
64
+ removed
65
+ ? `Signed out of ${context.origin}.`
66
+ : `Not signed in to ${context.origin}.`,
67
+ ]);
68
+ },
69
+ async whoami(context) {
70
+ const account = await (await client(context)).getAccount();
71
+ emit(context.writer, account, [
72
+ `Account ${account.id}`,
73
+ ...(account.name ? [account.name] : []),
74
+ ]);
75
+ },
76
+ async "project list"(context) {
77
+ const projects = await (await client(context)).listProjects();
78
+ emit(context.writer, projects, table([
79
+ ["ID", "SLUG", "NAME"],
80
+ ...projects.items.map((project) => [
81
+ project.id,
82
+ project.slug,
83
+ project.name,
84
+ ]),
85
+ ]));
86
+ },
87
+ async "project create"(context) {
88
+ const name = context.parsed.words[2];
89
+ if (!name)
90
+ throw new UsageError("wb project create <name> [--slug <slug>]");
91
+ const slug = context.parsed.options.get("slug") ??
92
+ name
93
+ .toLowerCase()
94
+ .replace(/[^a-z0-9]+/gu, "-")
95
+ .replace(/^-+|-+$/gu, "");
96
+ const project = await (await client(context)).createProject({ name, slug }, idempotency(context.parsed));
97
+ emit(context.writer, project, [
98
+ `Created project ${project.id} (${project.slug}).`,
99
+ ...table([
100
+ ["ENVIRONMENT", "KIND", "DEFAULT NOTIFIER"],
101
+ ...project.environments.map((environment) => [
102
+ environment.id,
103
+ environment.kind,
104
+ environment.default_notifier.id,
105
+ ]),
106
+ ]),
107
+ ]);
108
+ },
109
+ async "credential create"(context) {
110
+ const { environmentId, projectId } = environmentArguments(context.parsed);
111
+ const label = required(context.parsed, "label", "what this credential is for");
112
+ // The contract types the scope list as a bounded tuple, so a plain
113
+ // string[] cannot be passed. The values are validated by the API either
114
+ // way; the assertion buys the caller a shape the compiler accepts.
115
+ const scopes = (context.parsed.repeated.get("scope") ?? [
116
+ "messages:write",
117
+ "messages:read",
118
+ "subscriptions:write",
119
+ "subscriptions:read",
120
+ ]);
121
+ const created = await (await client(context)).createApiCredential(projectId, environmentId, { label, scopes });
122
+ if (context.writer.json) {
123
+ emit(context.writer, created, []);
124
+ return;
125
+ }
126
+ emitSecret(context.writer, created.secret, "The complete secret is shown once and is not retrievable later. Store it now.");
127
+ },
128
+ async "connection list"(context) {
129
+ const { environmentId, projectId } = environmentArguments(context.parsed);
130
+ const connections = await (await client(context)).listChannelConnections(projectId, environmentId);
131
+ emit(context.writer, connections, table([
132
+ ["ID", "MODE", "STATUS", "NAME"],
133
+ ...connections.items.map((connection) => [
134
+ connection.id,
135
+ connection.mode,
136
+ connection.status,
137
+ connection.display_name,
138
+ ]),
139
+ ]));
140
+ },
141
+ async "connection providers"(context) {
142
+ const { environmentId, projectId } = environmentArguments(context.parsed);
143
+ const providers = await (await client(context)).listConnectionProviders(projectId, environmentId);
144
+ emit(context.writer, providers, table([
145
+ ["PROVIDER", "SHARED IDENTITY", "DISPLAY NAME"],
146
+ ...providers.items.flatMap((provider) => provider.shared_identities.map((identity) => [
147
+ provider.provider,
148
+ identity.id,
149
+ identity.display_name,
150
+ ])),
151
+ ]));
152
+ },
153
+ async "connection create"(context) {
154
+ const { environmentId, projectId } = environmentArguments(context.parsed);
155
+ const mode = required(context.parsed, "mode", "whooshbang_shared, customer_managed, or customer_byok");
156
+ const displayName = required(context.parsed, "name", "what to call this connection");
157
+ let request = {
158
+ display_name: displayName,
159
+ mode,
160
+ };
161
+ if (mode === "whooshbang_shared") {
162
+ request["shared_identity_id"] = required(context.parsed, "shared-identity", "which shared identity to select — see `wb connection providers`");
163
+ }
164
+ else if (mode === "customer_byok") {
165
+ if (!context.parsed.switches.has("credential-stdin")) {
166
+ throw new UsageError("customer_byok needs --credential-stdin, and the token on standard input. A bot token passed as an argument is visible to every process on the machine and lands in your shell history.");
167
+ }
168
+ request = {
169
+ ...request,
170
+ confirm_webhook_takeover: true,
171
+ credential: (await context.readStdin()).trim(),
172
+ };
173
+ }
174
+ else if (mode === "customer_managed") {
175
+ const username = context.parsed.options.get("username");
176
+ if (username)
177
+ request["suggested_username"] = username;
178
+ }
179
+ const connection = await (await client(context)).createChannelConnection(projectId, environmentId, request, idempotency(context.parsed));
180
+ emit(context.writer, connection, [
181
+ `Created connection ${connection.id} (${connection.status}).`,
182
+ ...(connection.setup?.handoff_url
183
+ ? [`Finish setup in Telegram: ${connection.setup.handoff_url}`]
184
+ : []),
185
+ ]);
186
+ },
187
+ async "connection route"(context) {
188
+ const { environmentId, projectId } = environmentArguments(context.parsed);
189
+ const routes = await (await client(context)).attachNotifierConnection(projectId, environmentId, {
190
+ connection_id: required(context.parsed, "connection", "the connection to route through"),
191
+ notifier_id: required(context.parsed, "notifier", "the notifier to route"),
192
+ });
193
+ emit(context.writer, routes, table([
194
+ ["NOTIFIER", "CONNECTION", "DEFAULT"],
195
+ ...routes.items.map((route) => [
196
+ route.notifier_id,
197
+ route.connection_id,
198
+ route.is_default ? "yes" : "no",
199
+ ]),
200
+ ]));
201
+ },
202
+ async "link create"(context) {
203
+ const { environmentId, projectId } = environmentArguments(context.parsed);
204
+ const connectionId = context.parsed.options.get("connection");
205
+ const link = await (await client(context)).createSubscriptionLinkInEnvironment(projectId, environmentId, {
206
+ channels: ["telegram"],
207
+ ...(connectionId === undefined ? {} : { connection_id: connectionId }),
208
+ subscriber_id: required(context.parsed, "subscriber", "your own identifier for this subscriber"),
209
+ }, idempotency(context.parsed));
210
+ emit(context.writer, link, [
211
+ `Link ${link.id} (${link.status}).`,
212
+ // A link carries no authorization URL once it has been used, and an
213
+ // idempotent replay returns the used one. Printing "undefined" there
214
+ // would read as a defect rather than as the link having done its job.
215
+ ...(link.authorization_url
216
+ ? ["Send the subscriber here:", link.authorization_url]
217
+ : ["This link has already been used; issue another to invite again."]),
218
+ ]);
219
+ },
220
+ async "message send"(context) {
221
+ const { environmentId, projectId } = environmentArguments(context.parsed);
222
+ const message = await (await client(context)).createMessageInEnvironment(projectId, environmentId, {
223
+ content: {
224
+ text: required(context.parsed, "text", "the message body"),
225
+ type: "text",
226
+ },
227
+ delivery_target: deliveryTarget(context.parsed),
228
+ to: {
229
+ subscriber_id: required(context.parsed, "subscriber", "who to send to"),
230
+ },
231
+ }, idempotency(context.parsed));
232
+ emit(context.writer, message, [
233
+ `Accepted ${message.id} (${message.state}).`,
234
+ `Read it with: wb message get ${message.id} --project ${projectId} --environment ${environmentId}`,
235
+ ]);
236
+ },
237
+ async "message get"(context) {
238
+ const { environmentId, projectId } = environmentArguments(context.parsed);
239
+ const messageId = context.parsed.words[2];
240
+ if (!messageId)
241
+ throw new UsageError("wb message get <message-id>");
242
+ const message = await (await client(context)).getMessageInEnvironment(projectId, environmentId, messageId);
243
+ emit(context.writer, message, [
244
+ `${message.id} ${message.state}`,
245
+ `provider: ${message.delivery.highest_proven_provider_state}`,
246
+ `attempts: ${message.delivery.attempt_count}`,
247
+ ]);
248
+ },
249
+ };
250
+ export function resolveCommand(words) {
251
+ const two = words.slice(0, 2).join(" ");
252
+ const one = words[0] ?? "";
253
+ return commands[two] ?? commands[one] ?? null;
254
+ }
255
+ export const COMMAND_NAMES = Object.keys(commands);
256
+ export { resolveOrigin };
257
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Where the CLI keeps what it learned about a deployment.
3
+ *
4
+ * One file, holding one record per origin. A developer who works against
5
+ * staging and production is the ordinary case, not an exotic one, and a single
6
+ * unkeyed token would silently send staging work to production the first time
7
+ * somebody changed `--api-url`.
8
+ *
9
+ * `$XDG_CONFIG_HOME` is honoured where it is set, and `~/.config/whooshbang`
10
+ * otherwise, which is where this project already keeps operator files.
11
+ */
12
+ export interface StoredSession {
13
+ /** The client id dynamic registration returned. Reused on later logins. */
14
+ readonly clientId: string;
15
+ readonly accessToken: string;
16
+ readonly refreshToken?: string;
17
+ /** Epoch milliseconds, or absent when the server named no expiry. */
18
+ readonly expiresAt?: number;
19
+ readonly scopes: readonly string[];
20
+ }
21
+ export interface StoredCredentials {
22
+ readonly version: 1;
23
+ /** Keyed by API origin, exactly as the caller spelled it. */
24
+ readonly origins: Record<string, StoredSession>;
25
+ }
26
+ export declare function credentialsDirectory(environment?: NodeJS.ProcessEnv): string;
27
+ export declare function credentialsPath(environment?: NodeJS.ProcessEnv): string;
28
+ /**
29
+ * A missing or unreadable file is an empty store rather than an error: not
30
+ * being logged in is the ordinary first state, and a corrupted file should send
31
+ * somebody to `wb login` rather than to a stack trace.
32
+ */
33
+ export declare function readCredentials(file?: string): Promise<StoredCredentials>;
34
+ /**
35
+ * Written owner-only, in a directory created owner-only.
36
+ *
37
+ * `writeFile`'s mode is subject to the process umask, so the permissions are
38
+ * set explicitly afterwards rather than hoped for. The token is a bearer
39
+ * credential for the whole account; a group-readable home directory is common
40
+ * enough that leaving this to umask is not a defensible default.
41
+ */
42
+ export declare function writeCredentials(credentials: StoredCredentials, file?: string): Promise<void>;
43
+ export declare function storeSession(origin: string, session: StoredSession, file?: string): Promise<void>;
44
+ export declare function forgetSession(origin: string, file?: string): Promise<StoredSession | null>;
45
+ export declare function loadSession(origin: string, file?: string): Promise<StoredSession | null>;
@@ -0,0 +1,85 @@
1
+ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const EMPTY = { origins: {}, version: 1 };
5
+ export function credentialsDirectory(environment = process.env) {
6
+ const xdg = environment.XDG_CONFIG_HOME?.trim();
7
+ const base = xdg && xdg.length > 0 ? xdg : path.join(os.homedir(), ".config");
8
+ return path.join(base, "whooshbang");
9
+ }
10
+ export function credentialsPath(environment = process.env) {
11
+ return path.join(credentialsDirectory(environment), "cli-credentials.json");
12
+ }
13
+ /**
14
+ * A missing or unreadable file is an empty store rather than an error: not
15
+ * being logged in is the ordinary first state, and a corrupted file should send
16
+ * somebody to `wb login` rather than to a stack trace.
17
+ */
18
+ export async function readCredentials(file = credentialsPath()) {
19
+ let contents;
20
+ try {
21
+ contents = await readFile(file, "utf8");
22
+ }
23
+ catch {
24
+ return EMPTY;
25
+ }
26
+ try {
27
+ const parsed = JSON.parse(contents);
28
+ if (typeof parsed !== "object" ||
29
+ parsed === null ||
30
+ parsed.version !== 1 ||
31
+ typeof parsed.origins !== "object" ||
32
+ parsed.origins === null) {
33
+ return EMPTY;
34
+ }
35
+ return parsed;
36
+ }
37
+ catch {
38
+ return EMPTY;
39
+ }
40
+ }
41
+ /**
42
+ * Written owner-only, in a directory created owner-only.
43
+ *
44
+ * `writeFile`'s mode is subject to the process umask, so the permissions are
45
+ * set explicitly afterwards rather than hoped for. The token is a bearer
46
+ * credential for the whole account; a group-readable home directory is common
47
+ * enough that leaving this to umask is not a defensible default.
48
+ */
49
+ export async function writeCredentials(credentials, file = credentialsPath()) {
50
+ await mkdir(path.dirname(file), { mode: 0o700, recursive: true });
51
+ await chmod(path.dirname(file), 0o700).catch(() => undefined);
52
+ await writeFile(file, `${JSON.stringify(credentials, null, 2)}\n`, {
53
+ mode: 0o600,
54
+ });
55
+ await chmod(file, 0o600);
56
+ }
57
+ export async function storeSession(origin, session, file = credentialsPath()) {
58
+ const current = await readCredentials(file);
59
+ await writeCredentials({
60
+ origins: { ...current.origins, [origin]: session },
61
+ version: 1,
62
+ }, file);
63
+ }
64
+ export async function forgetSession(origin, file = credentialsPath()) {
65
+ const current = await readCredentials(file);
66
+ const session = current.origins[origin] ?? null;
67
+ if (!session)
68
+ return null;
69
+ const origins = { ...current.origins };
70
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
71
+ delete origins[origin];
72
+ if (Object.keys(origins).length === 0) {
73
+ // Nothing left to hold: remove the file rather than leave an empty husk
74
+ // that reads, to anybody auditing a home directory, like a live credential.
75
+ await rm(file, { force: true });
76
+ return session;
77
+ }
78
+ await writeCredentials({ origins, version: 1 }, file);
79
+ return session;
80
+ }
81
+ export async function loadSession(origin, file = credentialsPath()) {
82
+ const current = await readCredentials(file);
83
+ return current.origins[origin] ?? null;
84
+ }
85
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: readonly string[], overrides?: {
3
+ readonly credentialsFile?: string;
4
+ readonly openUrl?: (url: string) => Promise<void>;
5
+ }): Promise<number>;