@bussolabs/closeyourit-cli 0.0.21 โ 0.0.25
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/dist/base.d.ts +4 -0
- package/dist/base.js +5 -1
- package/dist/commands/agents/list.js +1 -1
- package/dist/commands/agents/runs/list.js +1 -1
- package/dist/commands/cron-monitors/list.js +1 -1
- package/dist/commands/errors/list.js +1 -1
- package/dist/commands/ideas/list.js +1 -1
- package/dist/commands/ideas/show.js +3 -3
- package/dist/commands/kb/ask.js +1 -1
- package/dist/commands/monitors/list.js +1 -1
- package/dist/commands/run.d.ts +12 -0
- package/dist/commands/run.js +33 -0
- package/dist/commands/secrets/delete.d.ts +13 -0
- package/dist/commands/secrets/delete.js +26 -0
- package/dist/commands/secrets/download.d.ts +12 -0
- package/dist/commands/secrets/download.js +41 -0
- package/dist/commands/secrets/get.d.ts +13 -0
- package/dist/commands/secrets/get.js +30 -0
- package/dist/commands/secrets/import.d.ts +22 -0
- package/dist/commands/secrets/import.js +71 -0
- package/dist/commands/secrets/list.d.ts +10 -0
- package/dist/commands/secrets/list.js +32 -0
- package/dist/commands/secrets/set.d.ts +15 -0
- package/dist/commands/secrets/set.js +36 -0
- package/dist/commands/secrets/sync.d.ts +9 -0
- package/dist/commands/secrets/sync.js +20 -0
- package/dist/commands/servers/list.js +1 -1
- package/dist/commands/tickets/list.js +1 -1
- package/dist/commands/tickets/show.js +8 -8
- package/dist/commands/tokens/list.js +1 -1
- package/dist/lib/output.d.ts +17 -1
- package/dist/lib/output.js +140 -5
- package/dist/lib/stdin.d.ts +3 -0
- package/dist/lib/stdin.js +11 -0
- package/dist/lib/subprocess.d.ts +6 -0
- package/dist/lib/subprocess.js +16 -0
- package/oclif.manifest.json +2315 -1857
- package/package.json +4 -1
package/dist/base.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export declare const UUID_RE: RegExp;
|
|
|
6
6
|
export declare const projectFlag: {
|
|
7
7
|
project: Interfaces.OptionFlag<string, Interfaces.CustomOptions>;
|
|
8
8
|
};
|
|
9
|
+
/** Shared `--environment <code|id>` flag (required). The backend resolves it within the project. */
|
|
10
|
+
export declare const environmentFlag: {
|
|
11
|
+
environment: Interfaces.OptionFlag<string, Interfaces.CustomOptions>;
|
|
12
|
+
};
|
|
9
13
|
/** Shared `--page` flag. */
|
|
10
14
|
export declare const pageFlag: {
|
|
11
15
|
page: Interfaces.OptionFlag<number, Interfaces.CustomOptions>;
|
package/dist/base.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BaseCommand = exports.pageFlag = exports.projectFlag = exports.UUID_RE = void 0;
|
|
3
|
+
exports.BaseCommand = exports.pageFlag = exports.environmentFlag = exports.projectFlag = exports.UUID_RE = void 0;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const config_1 = require("./lib/config");
|
|
6
6
|
const api_1 = require("./lib/api");
|
|
@@ -10,6 +10,10 @@ exports.UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
|
|
|
10
10
|
exports.projectFlag = {
|
|
11
11
|
project: core_1.Flags.string({ char: 'p', description: 'Project id (UUID) or key', required: true }),
|
|
12
12
|
};
|
|
13
|
+
/** Shared `--environment <code|id>` flag (required). The backend resolves it within the project. */
|
|
14
|
+
exports.environmentFlag = {
|
|
15
|
+
environment: core_1.Flags.string({ char: 'e', description: 'Environment code (e.g. production) or id', required: true }),
|
|
16
|
+
};
|
|
13
17
|
/** Shared `--page` flag. */
|
|
14
18
|
exports.pageFlag = {
|
|
15
19
|
page: core_1.Flags.integer({ description: 'Page number', default: 1 }),
|
|
@@ -24,7 +24,7 @@ class AgentsList extends base_1.BaseCommand {
|
|
|
24
24
|
String(agent.kind ?? ''),
|
|
25
25
|
String(agent.schedule ?? ''),
|
|
26
26
|
String(agent.enabled ?? ''),
|
|
27
|
-
|
|
27
|
+
agent.last_run_status ? (0, output_1.statusCell)(agent.last_run_status) : '-',
|
|
28
28
|
String(agent.id ?? ''),
|
|
29
29
|
])));
|
|
30
30
|
}
|
|
@@ -19,7 +19,7 @@ class AgentsRunsList extends base_1.BaseCommand {
|
|
|
19
19
|
const runs = res.data ?? [];
|
|
20
20
|
if (!this.jsonEnabled()) {
|
|
21
21
|
this.log((0, output_1.renderTable)(['STATUS', 'PROJECT', 'DURATION', 'STARTED', 'ID'], runs.map((run) => [
|
|
22
|
-
|
|
22
|
+
(0, output_1.statusCell)(run.status),
|
|
23
23
|
String(run.project_id ?? '-'),
|
|
24
24
|
run.duration_ms == null ? '-' : `${run.duration_ms} ms`,
|
|
25
25
|
String(run.started_at ?? '-'),
|
|
@@ -26,7 +26,7 @@ class CronMonitorsList extends base_1.BaseCommand {
|
|
|
26
26
|
if (!this.jsonEnabled()) {
|
|
27
27
|
this.log((0, output_1.renderTable)(['NAME', 'STATUS', 'INTERVAL (min)', 'LAST CHECK-IN', 'ID'], monitors.map((monitor) => [
|
|
28
28
|
String(monitor.name ?? ''),
|
|
29
|
-
|
|
29
|
+
(0, output_1.statusCell)(monitor.status),
|
|
30
30
|
String(monitor.expected_interval_minutes ?? '-'),
|
|
31
31
|
String(monitor.last_check_in_at ?? '-'),
|
|
32
32
|
String(monitor.id ?? ''),
|
|
@@ -26,7 +26,7 @@ class ErrorsList extends base_1.BaseCommand {
|
|
|
26
26
|
this.log((0, output_1.renderTable)(['ID', 'TITLE', 'STATUS', 'EVENTS'], groups.map((group) => [
|
|
27
27
|
String(group.id ?? ''),
|
|
28
28
|
String(group.title ?? ''),
|
|
29
|
-
|
|
29
|
+
(0, output_1.statusCell)(group.status),
|
|
30
30
|
String(group.events_count ?? ''),
|
|
31
31
|
])));
|
|
32
32
|
}
|
|
@@ -29,7 +29,7 @@ class IdeasList extends base_1.BaseCommand {
|
|
|
29
29
|
this.log((0, output_1.renderTable)(['ID', 'TITLE', 'STATUS', 'VOTES', 'COMMENTS'], ideas.map((idea) => [
|
|
30
30
|
String(idea.id ?? ''),
|
|
31
31
|
String(idea.title ?? ''),
|
|
32
|
-
|
|
32
|
+
(0, output_1.statusCell)(idea.status),
|
|
33
33
|
String(idea.votes_count ?? 0),
|
|
34
34
|
String(idea.comments_count ?? 0),
|
|
35
35
|
])));
|
|
@@ -18,14 +18,14 @@ class IdeasShow extends base_1.BaseCommand {
|
|
|
18
18
|
const { problem, solution, stakeholders, ...fields } = res.data ?? {};
|
|
19
19
|
this.log((0, output_1.renderRecord)(fields));
|
|
20
20
|
if (Array.isArray(stakeholders) && stakeholders.length > 0) {
|
|
21
|
-
this.log(`\
|
|
21
|
+
this.log(`\n๐ฅ Stakeholders: ${stakeholders.join(', ')}`);
|
|
22
22
|
}
|
|
23
23
|
if (problem) {
|
|
24
|
-
this.log('\
|
|
24
|
+
this.log('\n๐ฏ Problem:');
|
|
25
25
|
this.log((0, output_1.sanitizeMultiline)(String(problem)));
|
|
26
26
|
}
|
|
27
27
|
if (solution) {
|
|
28
|
-
this.log('\
|
|
28
|
+
this.log('\n๐ก Solution:');
|
|
29
29
|
this.log((0, output_1.sanitizeMultiline)(String(solution)));
|
|
30
30
|
}
|
|
31
31
|
}
|
package/dist/commands/kb/ask.js
CHANGED
|
@@ -33,7 +33,7 @@ class KbAsk extends base_1.BaseCommand {
|
|
|
33
33
|
this.log((0, output_1.sanitizeMultiline)(data.answer));
|
|
34
34
|
const pages = Array.isArray(data.pages) ? data.pages : [];
|
|
35
35
|
if (pages.length > 0) {
|
|
36
|
-
this.log('\
|
|
36
|
+
this.log('\n๐ Sources:');
|
|
37
37
|
for (const page of pages) {
|
|
38
38
|
this.log(` [${(0, output_1.sanitize)(page.kind)}] ${(0, output_1.sanitize)(page.title)} ยท ${(0, output_1.sanitize)(page.id)}`);
|
|
39
39
|
}
|
|
@@ -18,7 +18,7 @@ class MonitorsList extends base_1.BaseCommand {
|
|
|
18
18
|
this.log((0, output_1.renderTable)(['NAME', 'URL', 'STATUS', 'ACTIVE', 'ID'], monitors.map((monitor) => [
|
|
19
19
|
String(monitor.name ?? ''),
|
|
20
20
|
String(monitor.url ?? ''),
|
|
21
|
-
|
|
21
|
+
(0, output_1.statusCell)(monitor.current_status),
|
|
22
22
|
String(monitor.active ?? ''),
|
|
23
23
|
String(monitor.id ?? ''),
|
|
24
24
|
])));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../base';
|
|
2
|
+
export default class Run extends BaseCommand {
|
|
3
|
+
static strict: boolean;
|
|
4
|
+
static enableJsonFlag: boolean;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<unknown>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const base_1 = require("../base");
|
|
4
|
+
const subprocess_1 = require("../lib/subprocess");
|
|
5
|
+
class Run extends base_1.BaseCommand {
|
|
6
|
+
// Everything after `--` is the child command + its args (not parsed as flags).
|
|
7
|
+
static strict = false;
|
|
8
|
+
// The output belongs to the child process, not to us.
|
|
9
|
+
static enableJsonFlag = false;
|
|
10
|
+
static description = 'Run a command with the project/environment secrets injected as env vars (like `doppler run`). Secrets never touch disk.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> run -p acme-api -e production -- bundle exec rails server',
|
|
13
|
+
'<%= config.bin %> run -p acme-api -e production -- printenv DATABASE_URL',
|
|
14
|
+
];
|
|
15
|
+
static flags = {
|
|
16
|
+
...base_1.projectFlag,
|
|
17
|
+
...base_1.environmentFlag,
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { argv, flags } = await this.parse(Run);
|
|
21
|
+
const parts = argv;
|
|
22
|
+
if (parts.length === 0) {
|
|
23
|
+
this.error('Provide a command to run after `--`, e.g. `cyi run -p acme -e production -- printenv`', { exit: 1 });
|
|
24
|
+
}
|
|
25
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
26
|
+
const { data } = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/bundle?environment=${encodeURIComponent(flags.environment)}`);
|
|
27
|
+
const [command, ...commandArgs] = parts;
|
|
28
|
+
const code = await (0, subprocess_1.runCommand)(command, commandArgs, { ...process.env, ...(data ?? {}) });
|
|
29
|
+
// Propagate the child's exit code (0 on success) so `cyi run โฆ && next` behaves like the child.
|
|
30
|
+
this.exit(code);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.default = Run;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsDelete extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class SecretsDelete extends base_1.BaseCommand {
|
|
6
|
+
static args = {
|
|
7
|
+
name: core_1.Args.string({ description: 'Secret name', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static description = 'Delete a secret from a project environment';
|
|
10
|
+
static examples = ['<%= config.bin %> secrets delete -p acme-api -e production API_KEY'];
|
|
11
|
+
static flags = {
|
|
12
|
+
...base_1.projectFlag,
|
|
13
|
+
...base_1.environmentFlag,
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { args, flags } = await this.parse(SecretsDelete);
|
|
17
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
18
|
+
const name = args.name.toUpperCase();
|
|
19
|
+
await this.api.delete(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(name)}?environment=${encodeURIComponent(flags.environment)}`);
|
|
20
|
+
if (!this.jsonEnabled()) {
|
|
21
|
+
this.log(`Secret ${name} deleted (${flags.environment}).`);
|
|
22
|
+
}
|
|
23
|
+
return { deleted: name };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.default = SecretsDelete;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsDownload extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
out: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<unknown>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const promises_1 = require("node:fs/promises");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const base_1 = require("../../base");
|
|
6
|
+
class SecretsDownload extends base_1.BaseCommand {
|
|
7
|
+
static description = 'Download all secrets of an environment as .env or JSON (to a file or stdout). Values with newlines: use --format json. Prefer `cyi run` to avoid writing plaintext to disk.';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> secrets download -p acme-api -e production --out .env',
|
|
10
|
+
'<%= config.bin %> secrets download -p acme-api -e production --format json',
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
...base_1.projectFlag,
|
|
14
|
+
...base_1.environmentFlag,
|
|
15
|
+
format: core_1.Flags.string({ default: 'env', description: 'Output format', options: ['env', 'json'] }),
|
|
16
|
+
out: core_1.Flags.string({ description: 'Write to this file instead of stdout' }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { flags } = await this.parse(SecretsDownload);
|
|
20
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
21
|
+
const res = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/bundle?environment=${encodeURIComponent(flags.environment)}`);
|
|
22
|
+
const map = res.data ?? {};
|
|
23
|
+
const body = flags.format === 'json'
|
|
24
|
+
? JSON.stringify(map, null, 2)
|
|
25
|
+
: Object.entries(map)
|
|
26
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
if (flags.out) {
|
|
29
|
+
await (0, promises_1.writeFile)(flags.out, `${body}\n`);
|
|
30
|
+
if (!this.jsonEnabled()) {
|
|
31
|
+
this.log(`Wrote ${Object.keys(map).length} secrets โ ${flags.out}`);
|
|
32
|
+
this.warn('The file contains plaintext secrets. Prefer `cyi run` to inject them without touching disk.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (!this.jsonEnabled()) {
|
|
36
|
+
this.log(body);
|
|
37
|
+
}
|
|
38
|
+
return { count: Object.keys(map).length, format: flags.format, out: flags.out ?? null };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.default = SecretsDownload;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsGet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class SecretsGet extends base_1.BaseCommand {
|
|
6
|
+
static args = {
|
|
7
|
+
name: core_1.Args.string({ description: 'Secret name', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static description = 'Print the value of a single secret (pipeable). Requires the secrets.read permission.';
|
|
10
|
+
static examples = ['<%= config.bin %> secrets get -p acme-api -e production DATABASE_URL'];
|
|
11
|
+
static flags = {
|
|
12
|
+
...base_1.projectFlag,
|
|
13
|
+
...base_1.environmentFlag,
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { args, flags } = await this.parse(SecretsGet);
|
|
17
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
18
|
+
const res = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/bundle?environment=${encodeURIComponent(flags.environment)}`);
|
|
19
|
+
const map = res.data ?? {};
|
|
20
|
+
const key = args.name.toUpperCase();
|
|
21
|
+
if (!(key in map)) {
|
|
22
|
+
this.error(`Secret not found: ${key} (${flags.environment})`, { exit: 1 });
|
|
23
|
+
}
|
|
24
|
+
if (!this.jsonEnabled()) {
|
|
25
|
+
this.log(map[key]);
|
|
26
|
+
}
|
|
27
|
+
return { name: key, value: map[key] };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.default = SecretsGet;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
interface Entry {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Parse a .env text into entries: `KEY=VALUE` lines, skipping blanks and `#` comments. */
|
|
8
|
+
export declare function parseEnv(text: string): Entry[];
|
|
9
|
+
/** Parse JSON: either `{ "NAME": "value" }` or `[{ "name", "value", "description" }]`. */
|
|
10
|
+
export declare function parseJson(text: string): Entry[];
|
|
11
|
+
export default class SecretsImport extends BaseCommand {
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
14
|
+
static flags: {
|
|
15
|
+
'from-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
format: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
};
|
|
20
|
+
run(): Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseEnv = parseEnv;
|
|
4
|
+
exports.parseJson = parseJson;
|
|
5
|
+
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const base_1 = require("../../base");
|
|
8
|
+
const stdin_1 = require("../../lib/stdin");
|
|
9
|
+
/** Parse a .env text into entries: `KEY=VALUE` lines, skipping blanks and `#` comments. */
|
|
10
|
+
function parseEnv(text) {
|
|
11
|
+
const entries = [];
|
|
12
|
+
for (const raw of text.split('\n')) {
|
|
13
|
+
const line = raw.trim();
|
|
14
|
+
if (line.length === 0 || line.startsWith('#'))
|
|
15
|
+
continue;
|
|
16
|
+
const eq = line.indexOf('=');
|
|
17
|
+
if (eq === -1)
|
|
18
|
+
continue;
|
|
19
|
+
entries.push({ name: line.slice(0, eq).trim(), value: line.slice(eq + 1) });
|
|
20
|
+
}
|
|
21
|
+
return entries;
|
|
22
|
+
}
|
|
23
|
+
/** Parse JSON: either `{ "NAME": "value" }` or `[{ "name", "value", "description" }]`. */
|
|
24
|
+
function parseJson(text) {
|
|
25
|
+
const data = JSON.parse(text);
|
|
26
|
+
if (Array.isArray(data)) {
|
|
27
|
+
return data.map((row) => {
|
|
28
|
+
const item = row;
|
|
29
|
+
return { name: String(item.name ?? ''), value: String(item.value ?? ''), description: item.description };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return Object.entries(data).map(([name, value]) => ({ name, value: String(value) }));
|
|
33
|
+
}
|
|
34
|
+
class SecretsImport extends base_1.BaseCommand {
|
|
35
|
+
static description = 'Import many secrets at once (all-or-nothing) from a .env or JSON file';
|
|
36
|
+
static examples = [
|
|
37
|
+
'<%= config.bin %> secrets import -p acme-api -e production --from-file .env',
|
|
38
|
+
'<%= config.bin %> secrets import -p acme-api -e production --format json --from-file secrets.json',
|
|
39
|
+
'doppler secrets download --config production --no-file --format json | <%= config.bin %> secrets import -p acme-api -e production --format json',
|
|
40
|
+
];
|
|
41
|
+
static flags = {
|
|
42
|
+
...base_1.projectFlag,
|
|
43
|
+
...base_1.environmentFlag,
|
|
44
|
+
'from-file': core_1.Flags.string({ description: 'Path to a .env or JSON file (default: read from stdin)' }),
|
|
45
|
+
format: core_1.Flags.string({ default: 'env', description: 'Input format', options: ['env', 'json'] }),
|
|
46
|
+
};
|
|
47
|
+
async run() {
|
|
48
|
+
const { flags } = await this.parse(SecretsImport);
|
|
49
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
50
|
+
let text;
|
|
51
|
+
if (flags['from-file']) {
|
|
52
|
+
text = await (0, promises_1.readFile)(flags['from-file'], 'utf8');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
if (process.stdin.isTTY) {
|
|
56
|
+
this.error('Provide --from-file or pipe the input via stdin (e.g. `doppler secrets download --format json | cyi secrets import --format json`)', { exit: 1 });
|
|
57
|
+
}
|
|
58
|
+
text = await (0, stdin_1.readStream)();
|
|
59
|
+
}
|
|
60
|
+
const variables = flags.format === 'json' ? parseJson(text) : parseEnv(text);
|
|
61
|
+
if (variables.length === 0) {
|
|
62
|
+
this.error('No variables found in the input', { exit: 1 });
|
|
63
|
+
}
|
|
64
|
+
const res = await this.api.post(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/import`, { environment: flags.environment, variables });
|
|
65
|
+
if (!this.jsonEnabled()) {
|
|
66
|
+
this.log(`Imported ${res.data?.imported ?? variables.length} secrets (${flags.environment}).`);
|
|
67
|
+
}
|
|
68
|
+
return res;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.default = SecretsImport;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<unknown>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
const output_1 = require("../../lib/output");
|
|
6
|
+
class SecretsList extends base_1.BaseCommand {
|
|
7
|
+
static description = 'List secret names for a project (optionally filtered by environment). Never prints values.';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> secrets list -p acme-api',
|
|
10
|
+
'<%= config.bin %> secrets list -p acme-api -e production',
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
...base_1.projectFlag,
|
|
14
|
+
environment: core_1.Flags.string({ char: 'e', description: 'Filter by environment code or id' }),
|
|
15
|
+
};
|
|
16
|
+
async run() {
|
|
17
|
+
const { flags } = await this.parse(SecretsList);
|
|
18
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
19
|
+
const query = flags.environment ? `?environment=${encodeURIComponent(flags.environment)}` : '';
|
|
20
|
+
const res = await this.api.get(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets${query}`);
|
|
21
|
+
if (!this.jsonEnabled()) {
|
|
22
|
+
const rows = (res.data ?? []).map((secret) => [
|
|
23
|
+
String(secret.name ?? ''),
|
|
24
|
+
String(secret.environment?.code ?? ''),
|
|
25
|
+
String(secret.description ?? ''),
|
|
26
|
+
]);
|
|
27
|
+
this.log((0, output_1.renderTable)(['NAME', 'ENVIRONMENT', 'DESCRIPTION'], rows));
|
|
28
|
+
}
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.default = SecretsList;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsSet extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
assignment: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
value: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
environment: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<unknown>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class SecretsSet extends base_1.BaseCommand {
|
|
6
|
+
static args = {
|
|
7
|
+
assignment: core_1.Args.string({ description: 'NAME=VALUE, or just NAME with --value', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static description = 'Set (create or update) a secret for a project environment';
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> secrets set -p acme-api -e production DATABASE_URL=postgres://user:pass@host/db',
|
|
12
|
+
'<%= config.bin %> secrets set -p acme-api -e production API_KEY --value s3cr3t --description "Stripe key"',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
...base_1.projectFlag,
|
|
16
|
+
...base_1.environmentFlag,
|
|
17
|
+
value: core_1.Flags.string({ description: 'Secret value (use when the arg is just NAME)' }),
|
|
18
|
+
description: core_1.Flags.string({ description: 'Optional description' }),
|
|
19
|
+
};
|
|
20
|
+
async run() {
|
|
21
|
+
const { args, flags } = await this.parse(SecretsSet);
|
|
22
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
23
|
+
const eq = args.assignment.indexOf('=');
|
|
24
|
+
const name = eq === -1 ? args.assignment : args.assignment.slice(0, eq);
|
|
25
|
+
const value = eq === -1 ? flags.value : args.assignment.slice(eq + 1);
|
|
26
|
+
if (value === undefined) {
|
|
27
|
+
this.error('Provide a value: use NAME=VALUE or pass --value', { exit: 1 });
|
|
28
|
+
}
|
|
29
|
+
const res = await this.api.post(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets`, { environment: flags.environment, name, value, description: flags.description });
|
|
30
|
+
if (!this.jsonEnabled()) {
|
|
31
|
+
this.log(`Secret ${String(name).toUpperCase()} saved (${flags.environment}).`);
|
|
32
|
+
}
|
|
33
|
+
return res;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = SecretsSet;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SecretsSync extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<unknown>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const base_1 = require("../../base");
|
|
4
|
+
class SecretsSync extends base_1.BaseCommand {
|
|
5
|
+
static description = "Push the project's vault secrets to its connected GitHub repo Environment secrets (enqueues a background sync). Requires the github.manage permission.";
|
|
6
|
+
static examples = ['<%= config.bin %> secrets sync -p acme-api'];
|
|
7
|
+
static flags = {
|
|
8
|
+
...base_1.projectFlag,
|
|
9
|
+
};
|
|
10
|
+
async run() {
|
|
11
|
+
const { flags } = await this.parse(SecretsSync);
|
|
12
|
+
const projectId = await this.resolveProjectId(flags.project);
|
|
13
|
+
const res = await this.api.post(`/cli/v1/projects/${encodeURIComponent(projectId)}/secrets/sync`);
|
|
14
|
+
if (!this.jsonEnabled()) {
|
|
15
|
+
this.log(`Secret sync enqueued for project ${flags.project}.`);
|
|
16
|
+
}
|
|
17
|
+
return res;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.default = SecretsSync;
|
|
@@ -27,7 +27,7 @@ class ServersList extends base_1.BaseCommand {
|
|
|
27
27
|
if (!this.jsonEnabled()) {
|
|
28
28
|
this.log((0, output_1.renderTable)(['NAME', 'STATUS', 'CPU%', 'MEM%', 'DISK%', 'LAST SEEN', 'ID'], hosts.map((host) => [
|
|
29
29
|
String(host.name ?? ''),
|
|
30
|
-
|
|
30
|
+
(0, output_1.statusCell)(host.status),
|
|
31
31
|
String(host.cpu_pct ?? '-'),
|
|
32
32
|
String(host.mem_pct ?? '-'),
|
|
33
33
|
String(host.disk_pct ?? '-'),
|
|
@@ -15,7 +15,7 @@ class TicketsList extends base_1.BaseCommand {
|
|
|
15
15
|
this.log((0, output_1.renderTable)(['CODE', 'TITLE', 'STATUS'], tickets.map((ticket) => [
|
|
16
16
|
String(ticket.code ?? ''),
|
|
17
17
|
String(ticket.title ?? ''),
|
|
18
|
-
|
|
18
|
+
(0, output_1.statusCell)(ticket.status, typeof ticket.status_color === 'string' ? ticket.status_color : undefined),
|
|
19
19
|
])));
|
|
20
20
|
}
|
|
21
21
|
return res;
|
|
@@ -46,7 +46,7 @@ class TicketsShow extends base_1.BaseCommand {
|
|
|
46
46
|
return all;
|
|
47
47
|
}
|
|
48
48
|
renderAttachments(attachments) {
|
|
49
|
-
this.log(`\
|
|
49
|
+
this.log(`\n๐ Attachments (${attachments.length})`);
|
|
50
50
|
if (attachments.length === 0)
|
|
51
51
|
return;
|
|
52
52
|
this.log((0, output_1.renderTable)(['ID', 'FILENAME', 'CONTENT_TYPE', 'BYTE_SIZE', 'CREATED_AT'], attachments.map((attachment) => [
|
|
@@ -58,7 +58,7 @@ class TicketsShow extends base_1.BaseCommand {
|
|
|
58
58
|
])));
|
|
59
59
|
}
|
|
60
60
|
renderComments(comments) {
|
|
61
|
-
this.log(`\
|
|
61
|
+
this.log(`\n๐ฌ Comments (${comments.length})`);
|
|
62
62
|
for (const comment of comments) {
|
|
63
63
|
this.log(`\nโโ ${(0, output_1.sanitize)(comment.author ?? '?')} ยท ${(0, output_1.sanitize)(comment.created_at ?? '')} ยท ${(0, output_1.sanitize)(comment.id ?? '')}`);
|
|
64
64
|
this.log((0, output_1.sanitizeMultiline)(comment.body ?? ''));
|
|
@@ -90,13 +90,13 @@ class TicketsShow extends base_1.BaseCommand {
|
|
|
90
90
|
}));
|
|
91
91
|
// Body: description + technical analysis (only if present).
|
|
92
92
|
const scalarBlocks = [
|
|
93
|
-
['Description', ticket.description],
|
|
94
|
-
['Technical analysis', ticket.technical_analysis],
|
|
93
|
+
['๐', 'Description', ticket.description],
|
|
94
|
+
['๐ง', 'Technical analysis', ticket.technical_analysis],
|
|
95
95
|
];
|
|
96
|
-
for (const [label, value] of scalarBlocks) {
|
|
96
|
+
for (const [icon, label, value] of scalarBlocks) {
|
|
97
97
|
if (value === null || value === undefined || String(value).trim() === '')
|
|
98
98
|
continue;
|
|
99
|
-
this.log(
|
|
99
|
+
this.log((0, output_1.section)(label, icon));
|
|
100
100
|
this.log((0, output_1.sanitizeMultiline)(value));
|
|
101
101
|
}
|
|
102
102
|
this.renderScenarios(Array.isArray(ticket.scenarios) ? ticket.scenarios : []);
|
|
@@ -112,7 +112,7 @@ class TicketsShow extends base_1.BaseCommand {
|
|
|
112
112
|
];
|
|
113
113
|
scenarios.forEach((scenario, index) => {
|
|
114
114
|
const title = scenario.title ? `Scenario ${index + 1}: ${(0, output_1.sanitize)(scenario.title)}` : `Scenario ${index + 1}`;
|
|
115
|
-
this.log(
|
|
115
|
+
this.log((0, output_1.section)(title, '๐งช'));
|
|
116
116
|
for (const [label, key] of steps) {
|
|
117
117
|
const value = scenario[key];
|
|
118
118
|
if (value === null || value === undefined || String(value).trim() === '')
|
|
@@ -125,7 +125,7 @@ class TicketsShow extends base_1.BaseCommand {
|
|
|
125
125
|
renderConditions(conditions) {
|
|
126
126
|
if (conditions.length === 0)
|
|
127
127
|
return;
|
|
128
|
-
this.log('
|
|
128
|
+
this.log((0, output_1.section)('Definition of Done', 'โ
'));
|
|
129
129
|
for (const condition of conditions)
|
|
130
130
|
this.log(`- ${(0, output_1.sanitizeMultiline)(condition.text ?? '')}`);
|
|
131
131
|
}
|