envpass-cli 0.1.6 → 0.2.7
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 +5 -3
- package/dist/cli.js +58 -10
- package/dist/client.d.ts +39 -2
- package/dist/client.js +141 -3
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations.d.ts +21 -0
- package/dist/integrations.js +181 -0
- package/dist/schema.d.ts +2009 -389
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# envpass-cli
|
|
2
2
|
|
|
3
|
-
Client library and CLI for [envpass](https://envpass.dev): encrypted environment variables for every project
|
|
3
|
+
Client library and CLI for [envpass](https://envpass.dev): encrypted environment variables for every project and environment.
|
|
4
4
|
|
|
5
5
|
## CLI
|
|
6
6
|
|
|
@@ -10,6 +10,8 @@ bunx envpass-cli link -w acme -p shop -e dev # save defaults to .envpass.json
|
|
|
10
10
|
bunx envpass-cli pull # write .env
|
|
11
11
|
bunx envpass-cli pull -e prod -o - # print to stdout
|
|
12
12
|
bunx envpass-cli run -e dev -- bun dev # run with values injected, masked in output
|
|
13
|
+
bunx envpass-cli webhooks list # webhooks and syncs need `login`
|
|
14
|
+
bunx envpass-cli syncs run <id> # push a sync now
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
`npx envpass-cli …` works the same way.
|
|
@@ -25,7 +27,6 @@ ENVPASS_TOKEN=ep_live_… npx envpass-cli pull -e prod
|
|
|
25
27
|
| `-w, --workspace` | `ENVPASS_WORKSPACE` | Workspace slug (not needed with a service token) |
|
|
26
28
|
| `-p, --project` | `ENVPASS_PROJECT` | Project slug (not needed with a service token) |
|
|
27
29
|
| `-e, --env` | `ENVPASS_ENV` | Environment slug |
|
|
28
|
-
| `-s, --service` | `ENVPASS_SERVICE` | Service slug, or `shared` |
|
|
29
30
|
| `--url` | `ENVPASS_URL` | Server, default `https://envpass.dev` |
|
|
30
31
|
| | `ENVPASS_TOKEN` | Service token or CLI session token |
|
|
31
32
|
|
|
@@ -37,7 +38,8 @@ import { createEnvpass, loadEnv } from "envpass-cli"
|
|
|
37
38
|
await loadEnv({ environment: "prod" })
|
|
38
39
|
|
|
39
40
|
const envpass = createEnvpass({ token: process.env.ENVPASS_TOKEN })
|
|
40
|
-
const entries = await envpass.resolve({ environment: "prod"
|
|
41
|
+
const entries = await envpass.resolve({ environment: "prod" })
|
|
42
|
+
const webhooks = await envpass.listWebhooks({ workspace: "acme" })
|
|
41
43
|
|
|
42
44
|
const { data } = await envpass.api.GET("/api/v1/workspaces")
|
|
43
45
|
```
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,10 @@ import { parseArgs } from "node:util";
|
|
|
6
6
|
import { createEnvpass, EnvpassError } from "./client.js";
|
|
7
7
|
import { baseUrl, forgetToken, resolveScope, saveToken, storedToken, writeProjectLink, } from "./config.js";
|
|
8
8
|
import { deviceLogin, openBrowser } from "./device.js";
|
|
9
|
+
import { syncsCommand, webhooksCommand } from "./integrations.js";
|
|
9
10
|
import { maskingStream } from "./mask.js";
|
|
10
11
|
const { version } = createRequire(import.meta.url)("../package.json");
|
|
11
|
-
const HELP = `envpass ${version}: encrypted environment variables for every project
|
|
12
|
+
const HELP = `envpass ${version}: encrypted environment variables for every project and environment
|
|
12
13
|
|
|
13
14
|
Usage
|
|
14
15
|
envpass login Sign in through your browser
|
|
@@ -18,20 +19,41 @@ Usage
|
|
|
18
19
|
envpass pull [-e env] [-o .env] Write resolved values to a file ("-" for stdout)
|
|
19
20
|
envpass run [-e env] -- <command> Run a command with the values injected
|
|
20
21
|
|
|
22
|
+
Webhooks and syncs (need \`envpass login\`, not a service token)
|
|
23
|
+
envpass webhooks list List the workspace's webhooks
|
|
24
|
+
envpass webhooks add --url <url> Register a webhook and print its signing secret once
|
|
25
|
+
envpass webhooks remove <id> Delete a webhook
|
|
26
|
+
envpass webhooks test <id> Send a test event (exit code 1 if it fails)
|
|
27
|
+
envpass webhooks deliveries <id> Show a webhook's recent deliveries
|
|
28
|
+
envpass syncs list [-p project] List the project's syncs
|
|
29
|
+
envpass syncs run <id> Push a sync now
|
|
30
|
+
envpass syncs pause <id> Stop a sync from running
|
|
31
|
+
envpass syncs resume <id> Turn a paused sync back on
|
|
32
|
+
|
|
21
33
|
Options
|
|
22
34
|
-w, --workspace <slug> Workspace (not needed with a service token)
|
|
23
35
|
-p, --project <slug> Project (not needed with a service token)
|
|
24
36
|
-e, --env <slug> Environment, e.g. dev or prod
|
|
25
|
-
-s, --service <slug> Service layer, or "shared"
|
|
26
37
|
-o, --out <file> pull: output file (default .env)
|
|
27
38
|
-f, --format <format> pull: dotenv, shell or json (default dotenv)
|
|
28
39
|
--no-mask run: show secret values in the command's output
|
|
29
40
|
--no-browser login: print the link instead of opening a browser
|
|
41
|
+
--json webhooks list, webhooks deliveries, syncs list: print raw JSON
|
|
42
|
+
--limit <n> webhooks deliveries: how many to show, 1-100 (default 25)
|
|
30
43
|
--url <url> envpass server (default https://envpass.dev, or ENVPASS_URL)
|
|
31
44
|
|
|
45
|
+
Options for webhooks add
|
|
46
|
+
--url <url> Endpoint to POST to (point at your server with ENVPASS_URL)
|
|
47
|
+
--event <event> secrets.changed, sync.succeeded or sync.failed; repeat for more
|
|
48
|
+
(default secrets.changed and sync.failed)
|
|
49
|
+
-e, --env <slug> Only these environments; repeat for more (default all)
|
|
50
|
+
-p, --project <slug> Only these projects; repeat for more (default all)
|
|
51
|
+
-f, --format <format> json or slack (default slack for Slack URLs, json otherwise)
|
|
52
|
+
--description <text> A note to tell webhooks apart
|
|
53
|
+
|
|
32
54
|
Environment
|
|
33
55
|
ENVPASS_TOKEN A service token (ep_live_…) or CLI session token
|
|
34
|
-
ENVPASS_WORKSPACE, ENVPASS_PROJECT, ENVPASS_ENV
|
|
56
|
+
ENVPASS_WORKSPACE, ENVPASS_PROJECT, ENVPASS_ENV
|
|
35
57
|
`;
|
|
36
58
|
function fail(message, code = 1) {
|
|
37
59
|
process.stderr.write(`envpass: ${message}\n`);
|
|
@@ -43,12 +65,15 @@ async function main(argv) {
|
|
|
43
65
|
allowPositionals: true,
|
|
44
66
|
options: {
|
|
45
67
|
workspace: { type: "string", short: "w" },
|
|
46
|
-
project: { type: "string", short: "p" },
|
|
47
|
-
env: { type: "string", short: "e" },
|
|
48
|
-
service: { type: "string", short: "s" },
|
|
68
|
+
project: { type: "string", short: "p", multiple: true },
|
|
69
|
+
env: { type: "string", short: "e", multiple: true },
|
|
49
70
|
out: { type: "string", short: "o" },
|
|
50
71
|
format: { type: "string", short: "f" },
|
|
51
72
|
url: { type: "string" },
|
|
73
|
+
event: { type: "string", multiple: true },
|
|
74
|
+
description: { type: "string" },
|
|
75
|
+
limit: { type: "string" },
|
|
76
|
+
json: { type: "boolean" },
|
|
52
77
|
"no-mask": { type: "boolean" },
|
|
53
78
|
"no-browser": { type: "boolean" },
|
|
54
79
|
help: { type: "boolean", short: "h" },
|
|
@@ -60,12 +85,12 @@ async function main(argv) {
|
|
|
60
85
|
return void process.stdout.write(`${version}\n`);
|
|
61
86
|
if (values.help || !command || command === "help")
|
|
62
87
|
return void process.stdout.write(HELP);
|
|
63
|
-
const
|
|
88
|
+
const endpointUrl = command === "webhooks" && rest[0] === "add";
|
|
89
|
+
const url = baseUrl(endpointUrl ? undefined : values.url);
|
|
64
90
|
const flags = {
|
|
65
91
|
workspace: values.workspace,
|
|
66
|
-
project: values.project,
|
|
67
|
-
environment: values.env,
|
|
68
|
-
service: values.service,
|
|
92
|
+
project: values.project?.at(-1),
|
|
93
|
+
environment: values.env?.at(-1),
|
|
69
94
|
};
|
|
70
95
|
switch (command) {
|
|
71
96
|
case "login": {
|
|
@@ -172,6 +197,29 @@ async function main(argv) {
|
|
|
172
197
|
});
|
|
173
198
|
return;
|
|
174
199
|
}
|
|
200
|
+
case "webhooks":
|
|
201
|
+
case "syncs": {
|
|
202
|
+
const client = createEnvpass({ baseUrl: url });
|
|
203
|
+
if (!client.hasToken)
|
|
204
|
+
fail("Not signed in. Run `envpass login` or set ENVPASS_TOKEN.");
|
|
205
|
+
const run = command === "webhooks" ? webhooksCommand : syncsCommand;
|
|
206
|
+
process.exitCode = await run(client, {
|
|
207
|
+
scope: flags,
|
|
208
|
+
args: rest,
|
|
209
|
+
json: values.json,
|
|
210
|
+
url: endpointUrl ? values.url : undefined,
|
|
211
|
+
events: values.event,
|
|
212
|
+
environments: values.env,
|
|
213
|
+
projects: values.project,
|
|
214
|
+
format: values.format,
|
|
215
|
+
description: values.description,
|
|
216
|
+
limit: values.limit,
|
|
217
|
+
}, {
|
|
218
|
+
out: (text) => process.stdout.write(text),
|
|
219
|
+
err: (text) => process.stderr.write(text),
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
175
223
|
default:
|
|
176
224
|
fail(`Unknown command "${command}". Run \`envpass --help\`.`);
|
|
177
225
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import { type ProjectLink } from "./config.js";
|
|
2
2
|
import type { components, paths } from "./schema.js";
|
|
3
|
-
|
|
3
|
+
type Schemas = components["schemas"];
|
|
4
|
+
export type Source = Schemas["SecretSource"];
|
|
4
5
|
export type ResolvedEntry = {
|
|
5
6
|
key: string;
|
|
6
7
|
value: string;
|
|
7
8
|
source: Source;
|
|
8
9
|
};
|
|
9
10
|
export type ExportFormat = "dotenv" | "json" | "shell";
|
|
11
|
+
export type Workspace = Schemas["Workspace"];
|
|
12
|
+
export type Environment = Schemas["Environment"];
|
|
13
|
+
export type Webhook = Schemas["Webhook"];
|
|
14
|
+
export type WebhookEvent = Schemas["WebhookEvent"];
|
|
15
|
+
export type WebhookFormat = Webhook["format"];
|
|
16
|
+
export type WebhookDelivery = Schemas["WebhookDelivery"];
|
|
17
|
+
export type WebhookTestResult = Schemas["WebhookTestResult"];
|
|
18
|
+
export type Connection = Schemas["Connection"];
|
|
19
|
+
export type Sync = Schemas["Sync"];
|
|
20
|
+
export type NewWebhook = {
|
|
21
|
+
url: string;
|
|
22
|
+
events?: WebhookEvent[];
|
|
23
|
+
environments?: string[];
|
|
24
|
+
projects?: string[];
|
|
25
|
+
format?: WebhookFormat;
|
|
26
|
+
description?: string | null;
|
|
27
|
+
};
|
|
28
|
+
export declare const WEBHOOK_EVENTS: readonly WebhookEvent[];
|
|
10
29
|
export type EnvpassOptions = {
|
|
11
30
|
baseUrl?: string;
|
|
12
31
|
token?: string;
|
|
13
32
|
userAgent?: string;
|
|
33
|
+
fetch?: typeof fetch;
|
|
14
34
|
};
|
|
15
35
|
export declare class EnvpassError extends Error {
|
|
16
36
|
readonly status: number;
|
|
@@ -28,7 +48,6 @@ export declare function createEnvpass(options?: EnvpassOptions): {
|
|
|
28
48
|
id: string;
|
|
29
49
|
workspaceId: string;
|
|
30
50
|
projectId: string;
|
|
31
|
-
serviceId: string | null;
|
|
32
51
|
access: "read" | "write";
|
|
33
52
|
} | null;
|
|
34
53
|
workspaces: {
|
|
@@ -40,6 +59,23 @@ export declare function createEnvpass(options?: EnvpassOptions): {
|
|
|
40
59
|
}>;
|
|
41
60
|
resolve(scope?: ProjectLink): Promise<ResolvedEntry[]>;
|
|
42
61
|
resolveText(scope?: ProjectLink, format?: Exclude<ExportFormat, "json">): Promise<string>;
|
|
62
|
+
workspace(scope?: ProjectLink): Promise<Workspace>;
|
|
63
|
+
listEnvironments(scope?: ProjectLink): Promise<Environment[]>;
|
|
64
|
+
listWebhooks(scope?: ProjectLink): Promise<Webhook[]>;
|
|
65
|
+
createWebhook(input: NewWebhook, scope?: ProjectLink): Promise<{
|
|
66
|
+
webhook: Webhook;
|
|
67
|
+
secret: string;
|
|
68
|
+
}>;
|
|
69
|
+
deleteWebhook(id: string, scope?: ProjectLink): Promise<void>;
|
|
70
|
+
testWebhook(id: string, scope?: ProjectLink): Promise<WebhookTestResult>;
|
|
71
|
+
listWebhookDeliveries(id: string, options?: {
|
|
72
|
+
limit?: number;
|
|
73
|
+
before?: string;
|
|
74
|
+
}, scope?: ProjectLink): Promise<WebhookDelivery[]>;
|
|
75
|
+
listConnections(scope?: ProjectLink): Promise<Connection[]>;
|
|
76
|
+
listSyncs(scope?: ProjectLink): Promise<Sync[]>;
|
|
77
|
+
runSync(id: string, scope?: ProjectLink): Promise<void>;
|
|
78
|
+
setSyncEnabled(id: string, enabled: boolean, scope?: ProjectLink): Promise<void>;
|
|
43
79
|
};
|
|
44
80
|
export type Envpass = ReturnType<typeof createEnvpass>;
|
|
45
81
|
export declare function loadEnv(scope?: ProjectLink & EnvpassOptions & {
|
|
@@ -47,3 +83,4 @@ export declare function loadEnv(scope?: ProjectLink & EnvpassOptions & {
|
|
|
47
83
|
}): Promise<{
|
|
48
84
|
[k: string]: string;
|
|
49
85
|
}>;
|
|
86
|
+
export {};
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import createFetchClient from "openapi-fetch";
|
|
2
2
|
import { baseUrl, resolveScope, resolveToken } from "./config.js";
|
|
3
|
+
export const WEBHOOK_EVENTS = [
|
|
4
|
+
"secrets.changed",
|
|
5
|
+
"sync.succeeded",
|
|
6
|
+
"sync.failed",
|
|
7
|
+
];
|
|
8
|
+
const DEFAULT_WEBHOOK_EVENTS = ["secrets.changed", "sync.failed"];
|
|
3
9
|
export class EnvpassError extends Error {
|
|
4
10
|
status;
|
|
5
11
|
code;
|
|
@@ -14,13 +20,27 @@ function errorFrom(status, body) {
|
|
|
14
20
|
const error = body?.error;
|
|
15
21
|
return new EnvpassError(error?.message ?? `Request failed with status ${status}`, status, error?.code);
|
|
16
22
|
}
|
|
23
|
+
function unwrap(result) {
|
|
24
|
+
if (result.data === undefined)
|
|
25
|
+
throw errorFrom(result.response.status, result.error);
|
|
26
|
+
return result.data;
|
|
27
|
+
}
|
|
28
|
+
function pickIds(items, refs, kind, workspace) {
|
|
29
|
+
return refs.map((ref) => {
|
|
30
|
+
const item = items.find((candidate) => candidate.slug === ref || candidate.id === ref);
|
|
31
|
+
if (!item)
|
|
32
|
+
throw new EnvpassError(`No ${kind} "${ref}" in ${workspace.slug}`, 404, "not_found");
|
|
33
|
+
return item.id;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
17
36
|
export function createEnvpass(options = {}) {
|
|
18
37
|
const url = baseUrl(options.baseUrl);
|
|
19
38
|
const token = resolveToken(url, options.token);
|
|
20
39
|
const headers = { "user-agent": options.userAgent ?? "envpass-node" };
|
|
21
40
|
if (token)
|
|
22
41
|
headers.authorization = `Bearer ${token}`;
|
|
23
|
-
const
|
|
42
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
43
|
+
const api = createFetchClient({ baseUrl: url, headers, fetch: fetcher });
|
|
24
44
|
function scopeQuery(scope) {
|
|
25
45
|
if (!scope.environment) {
|
|
26
46
|
throw new EnvpassError("No environment. Pass --env, set ENVPASS_ENV, or run `envpass link`.", 400, "invalid");
|
|
@@ -29,9 +49,52 @@ export function createEnvpass(options = {}) {
|
|
|
29
49
|
workspace: scope.workspace,
|
|
30
50
|
project: scope.project,
|
|
31
51
|
environment: scope.environment,
|
|
32
|
-
service: scope.service,
|
|
33
52
|
};
|
|
34
53
|
}
|
|
54
|
+
function requireUser() {
|
|
55
|
+
if (token?.startsWith("ep_")) {
|
|
56
|
+
throw new EnvpassError("Webhooks and syncs need a user login, not a service token. Run `envpass login` and unset ENVPASS_TOKEN.", 403, "forbidden");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function workspaceFor(scope) {
|
|
60
|
+
requireUser();
|
|
61
|
+
const ref = resolveScope(scope).workspace;
|
|
62
|
+
const { workspaces } = unwrap(await api.GET("/api/v1/workspaces"));
|
|
63
|
+
if (ref) {
|
|
64
|
+
const match = workspaces.find((w) => w.slug === ref || w.id === ref);
|
|
65
|
+
if (!match)
|
|
66
|
+
throw new EnvpassError(`No workspace "${ref}" for this login`, 404, "not_found");
|
|
67
|
+
return match;
|
|
68
|
+
}
|
|
69
|
+
const [only] = workspaces;
|
|
70
|
+
if (only && workspaces.length === 1)
|
|
71
|
+
return only;
|
|
72
|
+
throw new EnvpassError("No workspace. Pass --workspace, set ENVPASS_WORKSPACE, or run `envpass link`.", 400, "invalid");
|
|
73
|
+
}
|
|
74
|
+
async function projectsOf(workspaceId) {
|
|
75
|
+
const path = { workspaceId };
|
|
76
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/projects", { params: { path } }))
|
|
77
|
+
.projects;
|
|
78
|
+
}
|
|
79
|
+
async function environmentsOf(workspaceId) {
|
|
80
|
+
const path = { workspaceId };
|
|
81
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/environments", { params: { path } })).environments;
|
|
82
|
+
}
|
|
83
|
+
async function projectFor(scope) {
|
|
84
|
+
const workspace = await workspaceFor(scope);
|
|
85
|
+
const ref = resolveScope(scope).project;
|
|
86
|
+
if (!ref) {
|
|
87
|
+
throw new EnvpassError("No project. Pass --project, set ENVPASS_PROJECT, or run `envpass link`.", 400, "invalid");
|
|
88
|
+
}
|
|
89
|
+
const [projectId = ref] = pickIds(await projectsOf(workspace.id), [ref], "project", workspace);
|
|
90
|
+
return { workspaceId: workspace.id, projectId };
|
|
91
|
+
}
|
|
92
|
+
async function webhookPath(webhookId, scope) {
|
|
93
|
+
return { workspaceId: (await workspaceFor(scope)).id, webhookId };
|
|
94
|
+
}
|
|
95
|
+
async function syncPath(syncId, scope) {
|
|
96
|
+
return { ...(await projectFor(scope)), syncId };
|
|
97
|
+
}
|
|
35
98
|
return {
|
|
36
99
|
url,
|
|
37
100
|
hasToken: Boolean(token),
|
|
@@ -53,7 +116,7 @@ export function createEnvpass(options = {}) {
|
|
|
53
116
|
},
|
|
54
117
|
async resolveText(scope = {}, format = "dotenv") {
|
|
55
118
|
const query = scopeQuery(resolveScope(scope));
|
|
56
|
-
const response = await
|
|
119
|
+
const response = await fetcher(`${url}/api/v1/resolve?${new URLSearchParams(Object.entries({ ...query, format }).filter((entry) => Boolean(entry[1])))}`, { headers });
|
|
57
120
|
const text = await response.text();
|
|
58
121
|
if (!response.ok) {
|
|
59
122
|
let body;
|
|
@@ -67,6 +130,81 @@ export function createEnvpass(options = {}) {
|
|
|
67
130
|
}
|
|
68
131
|
return text;
|
|
69
132
|
},
|
|
133
|
+
async workspace(scope = {}) {
|
|
134
|
+
return workspaceFor(scope);
|
|
135
|
+
},
|
|
136
|
+
async listEnvironments(scope = {}) {
|
|
137
|
+
return environmentsOf((await workspaceFor(scope)).id);
|
|
138
|
+
},
|
|
139
|
+
async listWebhooks(scope = {}) {
|
|
140
|
+
const path = { workspaceId: (await workspaceFor(scope)).id };
|
|
141
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/webhooks", { params: { path } })).webhooks;
|
|
142
|
+
},
|
|
143
|
+
async createWebhook(input, scope = {}) {
|
|
144
|
+
const workspace = await workspaceFor(scope);
|
|
145
|
+
const environments = input.environments ?? [];
|
|
146
|
+
const projects = input.projects ?? [];
|
|
147
|
+
const [environmentIds, projectIds] = await Promise.all([
|
|
148
|
+
environments.length
|
|
149
|
+
? environmentsOf(workspace.id).then((all) => pickIds(all, environments, "environment", workspace))
|
|
150
|
+
: [],
|
|
151
|
+
projects.length
|
|
152
|
+
? projectsOf(workspace.id).then((all) => pickIds(all, projects, "project", workspace))
|
|
153
|
+
: [],
|
|
154
|
+
]);
|
|
155
|
+
return unwrap(await api.POST("/api/v1/workspaces/{workspaceId}/webhooks", {
|
|
156
|
+
params: { path: { workspaceId: workspace.id } },
|
|
157
|
+
body: {
|
|
158
|
+
url: input.url,
|
|
159
|
+
events: input.events?.length ? input.events : DEFAULT_WEBHOOK_EVENTS,
|
|
160
|
+
environmentIds,
|
|
161
|
+
projectIds,
|
|
162
|
+
format: input.format,
|
|
163
|
+
description: input.description,
|
|
164
|
+
},
|
|
165
|
+
}));
|
|
166
|
+
},
|
|
167
|
+
async deleteWebhook(id, scope = {}) {
|
|
168
|
+
const path = await webhookPath(id, scope);
|
|
169
|
+
unwrap(await api.DELETE("/api/v1/workspaces/{workspaceId}/webhooks/{webhookId}", {
|
|
170
|
+
params: { path },
|
|
171
|
+
}));
|
|
172
|
+
},
|
|
173
|
+
async testWebhook(id, scope = {}) {
|
|
174
|
+
const path = await webhookPath(id, scope);
|
|
175
|
+
return unwrap(await api.POST("/api/v1/workspaces/{workspaceId}/webhooks/{webhookId}/test", {
|
|
176
|
+
params: { path },
|
|
177
|
+
}));
|
|
178
|
+
},
|
|
179
|
+
async listWebhookDeliveries(id, options = {}, scope = {}) {
|
|
180
|
+
const path = await webhookPath(id, scope);
|
|
181
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/webhooks/{webhookId}/deliveries", {
|
|
182
|
+
params: { path, query: options },
|
|
183
|
+
})).deliveries;
|
|
184
|
+
},
|
|
185
|
+
async listConnections(scope = {}) {
|
|
186
|
+
const path = { workspaceId: (await workspaceFor(scope)).id };
|
|
187
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/connections", { params: { path } })).connections;
|
|
188
|
+
},
|
|
189
|
+
async listSyncs(scope = {}) {
|
|
190
|
+
const path = await projectFor(scope);
|
|
191
|
+
return unwrap(await api.GET("/api/v1/workspaces/{workspaceId}/projects/{projectId}/syncs", {
|
|
192
|
+
params: { path },
|
|
193
|
+
})).syncs;
|
|
194
|
+
},
|
|
195
|
+
async runSync(id, scope = {}) {
|
|
196
|
+
const path = await syncPath(id, scope);
|
|
197
|
+
unwrap(await api.POST("/api/v1/workspaces/{workspaceId}/projects/{projectId}/syncs/{syncId}/run", {
|
|
198
|
+
params: { path },
|
|
199
|
+
}));
|
|
200
|
+
},
|
|
201
|
+
async setSyncEnabled(id, enabled, scope = {}) {
|
|
202
|
+
const path = await syncPath(id, scope);
|
|
203
|
+
unwrap(await api.PATCH("/api/v1/workspaces/{workspaceId}/projects/{projectId}/syncs/{syncId}", {
|
|
204
|
+
params: { path },
|
|
205
|
+
body: { enabled },
|
|
206
|
+
}));
|
|
207
|
+
},
|
|
70
208
|
};
|
|
71
209
|
}
|
|
72
210
|
export async function loadEnv(scope = {}) {
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -82,6 +82,5 @@ export function resolveScope(flags, start) {
|
|
|
82
82
|
workspace: flags.workspace ?? process.env.ENVPASS_WORKSPACE ?? file.workspace,
|
|
83
83
|
project: flags.project ?? process.env.ENVPASS_PROJECT ?? file.project,
|
|
84
84
|
environment: flags.environment ?? process.env.ENVPASS_ENV ?? file.environment,
|
|
85
|
-
service: flags.service ?? process.env.ENVPASS_SERVICE ?? file.service,
|
|
86
85
|
};
|
|
87
86
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createEnvpass, type Envpass, EnvpassError, type EnvpassOptions, type ExportFormat, loadEnv, type ResolvedEntry, type Source, } from "./client.js";
|
|
1
|
+
export { type Connection, createEnvpass, type Envpass, EnvpassError, type EnvpassOptions, type Environment, type ExportFormat, loadEnv, type NewWebhook, type ResolvedEntry, type Source, type Sync, WEBHOOK_EVENTS, type Webhook, type WebhookDelivery, type WebhookEvent, type WebhookFormat, type WebhookTestResult, type Workspace, } from "./client.js";
|
|
2
2
|
export { DEFAULT_URL, type ProjectLink } from "./config.js";
|
|
3
3
|
export { maskText } from "./mask.js";
|
|
4
4
|
export type { components, paths } from "./schema.js";
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Envpass } from "./client.js";
|
|
2
|
+
import type { ProjectLink } from "./config.js";
|
|
3
|
+
export type Io = {
|
|
4
|
+
out: (text: string) => void;
|
|
5
|
+
err: (text: string) => void;
|
|
6
|
+
};
|
|
7
|
+
export type IntegrationArgs = {
|
|
8
|
+
scope: ProjectLink;
|
|
9
|
+
args: string[];
|
|
10
|
+
json?: boolean;
|
|
11
|
+
url?: string;
|
|
12
|
+
events?: string[];
|
|
13
|
+
environments?: string[];
|
|
14
|
+
projects?: string[];
|
|
15
|
+
format?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
limit?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function table(header: string[], rows: string[][]): string;
|
|
20
|
+
export declare function webhooksCommand(client: Envpass, options: IntegrationArgs, io: Io): Promise<number>;
|
|
21
|
+
export declare function syncsCommand(client: Envpass, options: IntegrationArgs, io: Io): Promise<number>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { WEBHOOK_EVENTS, } from "./client.js";
|
|
2
|
+
const WEBHOOKS_USAGE = "Usage: envpass webhooks <list|add|remove|test|deliveries>";
|
|
3
|
+
const SYNCS_USAGE = "Usage: envpass syncs <list|run|pause|resume>";
|
|
4
|
+
export function table(header, rows) {
|
|
5
|
+
const all = [header, ...rows];
|
|
6
|
+
const widths = header.map((_, i) => Math.max(...all.map((row) => (row[i] ?? "").length)));
|
|
7
|
+
return all
|
|
8
|
+
.map((row) => row.map((cell, i) => (i === row.length - 1 ? cell : cell.padEnd(widths[i] ?? 0))).join(" "))
|
|
9
|
+
.join("\n")
|
|
10
|
+
.concat("\n");
|
|
11
|
+
}
|
|
12
|
+
function oneLine(text) {
|
|
13
|
+
return text ? text.replace(/\s+/g, " ").trim() : "-";
|
|
14
|
+
}
|
|
15
|
+
function json(value) {
|
|
16
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
17
|
+
}
|
|
18
|
+
function requireId(args, usage) {
|
|
19
|
+
const [id] = args;
|
|
20
|
+
if (!id)
|
|
21
|
+
throw new Error(usage);
|
|
22
|
+
return id;
|
|
23
|
+
}
|
|
24
|
+
function describeTest(result) {
|
|
25
|
+
const status = result.responseStatus === null ? "no response" : `HTTP ${result.responseStatus}`;
|
|
26
|
+
const detail = result.error ? `: ${oneLine(result.error)}` : "";
|
|
27
|
+
return `${result.ok ? "Delivered" : "Failed"}: ${status} in ${result.durationMs}ms${detail}\n`;
|
|
28
|
+
}
|
|
29
|
+
function parseLimit(limit) {
|
|
30
|
+
if (limit === undefined)
|
|
31
|
+
return undefined;
|
|
32
|
+
const value = Number(limit);
|
|
33
|
+
if (!/^[0-9]+$/.test(limit) || value < 1 || value > 100)
|
|
34
|
+
throw new Error("--limit must be a whole number from 1 to 100");
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function parseEvents(events) {
|
|
38
|
+
if (!events?.length)
|
|
39
|
+
return undefined;
|
|
40
|
+
return events.map((event) => {
|
|
41
|
+
const known = WEBHOOK_EVENTS.find((candidate) => candidate === event);
|
|
42
|
+
if (!known)
|
|
43
|
+
throw new Error(`--event must be one of ${WEBHOOK_EVENTS.join(", ")}`);
|
|
44
|
+
return known;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function parseFormat(format) {
|
|
48
|
+
if (format === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
if (format !== "json" && format !== "slack")
|
|
51
|
+
throw new Error("--format must be json or slack");
|
|
52
|
+
return format;
|
|
53
|
+
}
|
|
54
|
+
export async function webhooksCommand(client, options, io) {
|
|
55
|
+
const [action, ...args] = options.args;
|
|
56
|
+
const scope = { workspace: options.scope.workspace };
|
|
57
|
+
switch (action) {
|
|
58
|
+
case "list": {
|
|
59
|
+
const [webhooks, environments] = await Promise.all([
|
|
60
|
+
client.listWebhooks(scope),
|
|
61
|
+
client.listEnvironments(scope),
|
|
62
|
+
]);
|
|
63
|
+
if (options.json) {
|
|
64
|
+
io.out(json(webhooks));
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
if (!webhooks.length) {
|
|
68
|
+
io.err("No webhooks.\n");
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
const slugs = new Map(environments.map((e) => [e.id, e.slug]));
|
|
72
|
+
io.out(table(["ID", "URL", "EVENTS", "ENVIRONMENTS", "ENABLED", "LAST DELIVERY"], webhooks.map((hook) => [
|
|
73
|
+
hook.id,
|
|
74
|
+
hook.url,
|
|
75
|
+
hook.events.join(","),
|
|
76
|
+
hook.environmentIds.length
|
|
77
|
+
? hook.environmentIds.map((id) => slugs.get(id) ?? id).join(",")
|
|
78
|
+
: "all",
|
|
79
|
+
hook.enabled ? "on" : "off",
|
|
80
|
+
hook.lastDelivery
|
|
81
|
+
? [hook.lastDelivery.status, hook.lastDelivery.responseStatus ?? ""].join(" ").trim()
|
|
82
|
+
: "-",
|
|
83
|
+
])));
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
case "add": {
|
|
87
|
+
if (!options.url)
|
|
88
|
+
throw new Error("Usage: envpass webhooks add --url <url> [options]");
|
|
89
|
+
const { webhook, secret } = await client.createWebhook({
|
|
90
|
+
url: options.url,
|
|
91
|
+
events: parseEvents(options.events),
|
|
92
|
+
environments: options.environments,
|
|
93
|
+
projects: options.projects,
|
|
94
|
+
format: parseFormat(options.format),
|
|
95
|
+
description: options.description,
|
|
96
|
+
}, scope);
|
|
97
|
+
io.out(`Webhook ${webhook.id} created for ${webhook.url}\n`);
|
|
98
|
+
io.out(`Signing secret: ${secret}\n`);
|
|
99
|
+
io.err("Store the signing secret now: it is shown only once. Use it to verify the envpass-signature header.\n");
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
case "remove": {
|
|
103
|
+
const id = requireId(args, "Usage: envpass webhooks remove <id>");
|
|
104
|
+
await client.deleteWebhook(id, scope);
|
|
105
|
+
io.err(`Removed webhook ${id}\n`);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
case "test": {
|
|
109
|
+
const id = requireId(args, "Usage: envpass webhooks test <id>");
|
|
110
|
+
const result = await client.testWebhook(id, scope);
|
|
111
|
+
io.out(describeTest(result));
|
|
112
|
+
return result.ok ? 0 : 1;
|
|
113
|
+
}
|
|
114
|
+
case "deliveries": {
|
|
115
|
+
const id = requireId(args, "Usage: envpass webhooks deliveries <id> [--limit N]");
|
|
116
|
+
const deliveries = await client.listWebhookDeliveries(id, { limit: parseLimit(options.limit) }, scope);
|
|
117
|
+
if (options.json) {
|
|
118
|
+
io.out(json(deliveries));
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
if (!deliveries.length) {
|
|
122
|
+
io.err("No deliveries yet.\n");
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
io.out(table(["ID", "EVENT", "STATUS", "CODE", "ATTEMPTS", "DURATION", "CREATED", "ERROR"], deliveries.map((delivery) => [
|
|
126
|
+
delivery.id,
|
|
127
|
+
delivery.eventType,
|
|
128
|
+
delivery.status,
|
|
129
|
+
delivery.responseStatus === null ? "-" : String(delivery.responseStatus),
|
|
130
|
+
String(delivery.attempts),
|
|
131
|
+
delivery.durationMs === null ? "-" : `${delivery.durationMs}ms`,
|
|
132
|
+
delivery.createdAt,
|
|
133
|
+
oneLine(delivery.error),
|
|
134
|
+
])));
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(WEBHOOKS_USAGE);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function syncsCommand(client, options, io) {
|
|
142
|
+
const [action, ...args] = options.args;
|
|
143
|
+
const scope = { workspace: options.scope.workspace, project: options.scope.project };
|
|
144
|
+
switch (action) {
|
|
145
|
+
case "list": {
|
|
146
|
+
const syncs = await client.listSyncs(scope);
|
|
147
|
+
if (options.json) {
|
|
148
|
+
io.out(json(syncs));
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
if (!syncs.length) {
|
|
152
|
+
io.err("No syncs.\n");
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
io.out(table(["ID", "DESTINATION", "ENVIRONMENT", "STATUS", "LAST SYNCED", "LAST ERROR"], syncs.map((sync) => [
|
|
156
|
+
sync.id,
|
|
157
|
+
sync.destination,
|
|
158
|
+
sync.environmentSlug,
|
|
159
|
+
sync.enabled ? sync.status : "paused",
|
|
160
|
+
sync.lastSyncedAt ?? "never",
|
|
161
|
+
oneLine(sync.lastError),
|
|
162
|
+
])));
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
case "run": {
|
|
166
|
+
const id = requireId(args, "Usage: envpass syncs run <id>");
|
|
167
|
+
await client.runSync(id, scope);
|
|
168
|
+
io.err(`Queued sync ${id}\n`);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
case "pause":
|
|
172
|
+
case "resume": {
|
|
173
|
+
const id = requireId(args, `Usage: envpass syncs ${action} <id>`);
|
|
174
|
+
await client.setSyncEnabled(id, action === "resume", scope);
|
|
175
|
+
io.err(`${action === "resume" ? "Resumed" : "Paused"} sync ${id}\n`);
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
default:
|
|
179
|
+
throw new Error(SYNCS_USAGE);
|
|
180
|
+
}
|
|
181
|
+
}
|