@bussolabs/closeyourit-cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -81,6 +81,9 @@ CLOSEYOURIT_TOKEN=cyi_u_… CLOSEYOURIT_API_URL=https://www.closeyour.it \
81
81
  | `errors resolve\|reopen\|mute\|unmute <id> --project <id\|key>` | Triage an error group. |
82
82
  | `tokens list --project <id\|key>` | List ingest tokens. |
83
83
  | `tokens create --project <id\|key> --name <name> --environment-id <id>` | Create a token (secret shown once). |
84
+ | `tokens provision --project <id\|key> --name <name> --environment-id <code\|id> --to-project <id\|key> --to-environment-id <code\|id> --secret-name <NAME>` | Generate and deliver a token without revealing its value. GitHub sync is enabled by default; use `--no-sync-github` for vault-only delivery. |
85
+ | `tokens provisions show <id> --project <id\|key>` | Show secret-free provisioning status. |
86
+ | `tokens provisions retry <id> --project <id\|key>` | Retry GitHub sync without regenerating or revealing the token. |
84
87
  | `tokens rotate <id> --project <id\|key>` | Rotate a token (new secret shown once). |
85
88
  | `tokens revoke <id> --project <id\|key>` | Revoke a token. |
86
89
  | `secrets assets list --project <id\|key>` | List encrypted secret-file metadata (never contents). |
@@ -0,0 +1,18 @@
1
+ import { BaseCommand } from '../../base';
2
+ export default class TokensProvision extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ 'environment-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ scope: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ 'to-project': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ 'to-environment-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ 'secret-name': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ 'idempotency-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ 'sync-github': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
+ 'confirm-production': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
+ project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
16
+ };
17
+ run(): Promise<unknown>;
18
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const node_crypto_1 = require("node:crypto");
5
+ const base_1 = require("../../base");
6
+ const token_provision_1 = require("../../lib/token-provision");
7
+ class TokensProvision extends base_1.BaseCommand {
8
+ static description = 'Generate an ingest token and deliver it without revealing the secret';
9
+ static examples = [
10
+ '<%= config.bin %> tokens provision -p CYRA --name "Self monitoring" --environment-id staging --to-project CYRA --to-environment-id staging --secret-name CLOSEYOURIT_TOKEN',
11
+ ];
12
+ static flags = {
13
+ ...base_1.projectFlag,
14
+ name: core_1.Flags.string({ description: 'Human-readable token name', required: true }),
15
+ 'environment-id': core_1.Flags.string({ description: 'Source environment code or id', required: true }),
16
+ scope: core_1.Flags.string({ description: 'Token scope (repeatable)', multiple: true, options: ['ingest', 'read'] }),
17
+ 'to-project': core_1.Flags.string({ description: 'Destination project id or key', required: true }),
18
+ 'to-environment-id': core_1.Flags.string({ description: 'Destination environment code or id', required: true }),
19
+ 'secret-name': core_1.Flags.string({ description: 'Destination vault variable name', required: true }),
20
+ 'idempotency-key': core_1.Flags.string({ description: 'Stable key for safe request retries' }),
21
+ 'sync-github': core_1.Flags.boolean({ description: 'Synchronize the destination vault to GitHub', default: true, allowNo: true }),
22
+ 'confirm-production': core_1.Flags.boolean({ description: 'Required when source or destination is production' }),
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(TokensProvision);
26
+ const [projectId, destinationProjectId] = await Promise.all([
27
+ this.resolveProjectId(flags.project),
28
+ this.resolveProjectId(flags['to-project']),
29
+ ]);
30
+ const response = await this.api.post(`/cli/v1/projects/${projectId}/tokens/provisions`, {
31
+ name: flags.name,
32
+ environment_id: flags['environment-id'],
33
+ scopes: flags.scope?.length ? flags.scope : ['ingest'],
34
+ idempotency_key: flags['idempotency-key'] ?? (0, node_crypto_1.randomUUID)(),
35
+ sync_github: flags['sync-github'],
36
+ confirm_production: flags['confirm-production'] ?? false,
37
+ destination: {
38
+ project_id: destinationProjectId,
39
+ environment_id: flags['to-environment-id'],
40
+ secret_name: flags['secret-name'],
41
+ },
42
+ });
43
+ const safe = (0, token_provision_1.safeTokenProvisionEnvelope)(response);
44
+ if (!this.jsonEnabled()) {
45
+ const data = safe.data;
46
+ this.log('Token generated and delivered without revealing its value.');
47
+ this.log(` provision: ${data.provision?.id ?? '-'}`);
48
+ this.log(` token: ${data.token?.id ?? '-'}`);
49
+ this.log(` prefix: ${data.token?.token_prefix ?? '-'}`);
50
+ this.log(` status: ${data.provision?.status ?? '-'}`);
51
+ }
52
+ return safe;
53
+ }
54
+ }
55
+ exports.default = TokensProvision;
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../../base';
2
+ export default class TokensProvisionsRetry extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<unknown>;
11
+ }
@@ -0,0 +1,25 @@
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 token_provision_1 = require("../../../lib/token-provision");
6
+ class TokensProvisionsRetry extends base_1.BaseCommand {
7
+ static description = 'Retry GitHub synchronization without regenerating or revealing the token';
8
+ static args = {
9
+ id: core_1.Args.string({ description: 'Provision id', required: true }),
10
+ };
11
+ static flags = { ...base_1.projectFlag };
12
+ async run() {
13
+ const { args, flags } = await this.parse(TokensProvisionsRetry);
14
+ const projectId = await this.resolveProjectId(flags.project);
15
+ const response = await this.api.post(`/cli/v1/projects/${projectId}/tokens/provisions/${args.id}/retry`);
16
+ const safe = (0, token_provision_1.safeTokenProvisionEnvelope)(response);
17
+ if (!this.jsonEnabled()) {
18
+ const provision = safe.data;
19
+ this.log(`Provision ${provision.id ?? args.id} queued again.`);
20
+ this.log(` status: ${provision.status ?? '-'}`);
21
+ }
22
+ return safe;
23
+ }
24
+ }
25
+ exports.default = TokensProvisionsRetry;
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../../base';
2
+ export default class TokensProvisionsShow extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<unknown>;
11
+ }
@@ -0,0 +1,29 @@
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 token_provision_1 = require("../../../lib/token-provision");
6
+ class TokensProvisionsShow extends base_1.BaseCommand {
7
+ static description = 'Show secret-free token provision status';
8
+ static args = {
9
+ id: core_1.Args.string({ description: 'Provision id', required: true }),
10
+ };
11
+ static flags = { ...base_1.projectFlag };
12
+ async run() {
13
+ const { args, flags } = await this.parse(TokensProvisionsShow);
14
+ const projectId = await this.resolveProjectId(flags.project);
15
+ const response = await this.api.get(`/cli/v1/projects/${projectId}/tokens/provisions/${args.id}`);
16
+ const safe = (0, token_provision_1.safeTokenProvisionEnvelope)(response);
17
+ if (!this.jsonEnabled()) {
18
+ const data = safe.data;
19
+ this.log(`Provision ${data.provision?.id ?? args.id}`);
20
+ this.log(` status: ${data.provision?.status ?? '-'}`);
21
+ this.log(` destination: ${data.provision?.destination_project_id ?? '-'} / ${data.provision?.destination_environment_id ?? '-'}`);
22
+ this.log(` secret name: ${data.provision?.secret_name ?? '-'}`);
23
+ if (data.provision?.error_code)
24
+ this.log(` error: ${data.provision.error_code}`);
25
+ }
26
+ return safe;
27
+ }
28
+ }
29
+ exports.default = TokensProvisionsShow;
@@ -0,0 +1,8 @@
1
+ import type { Envelope } from './api';
2
+ type UnknownRecord = Record<string, unknown>;
3
+ /**
4
+ * Rebuild the backend response from an allow-list. Even if a future backend regression adds a
5
+ * plaintext field, blind commands cannot return it through oclif's JSON formatter.
6
+ */
7
+ export declare function safeTokenProvisionEnvelope(response: Envelope<unknown>): Envelope<UnknownRecord>;
8
+ export {};
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safeTokenProvisionEnvelope = safeTokenProvisionEnvelope;
4
+ const tokenKeys = [
5
+ 'id',
6
+ 'name',
7
+ 'environment_id',
8
+ 'token_prefix',
9
+ 'public_key',
10
+ 'scopes',
11
+ 'last_used_at',
12
+ 'revoked_at',
13
+ 'created_at',
14
+ ];
15
+ const provisionKeys = [
16
+ 'id',
17
+ 'status',
18
+ 'secret_name',
19
+ 'sync_github',
20
+ 'error_code',
21
+ 'error_message',
22
+ 'synced_at',
23
+ 'created_at',
24
+ 'updated_at',
25
+ 'source_project_id',
26
+ 'source_environment_id',
27
+ 'destination_project_id',
28
+ 'destination_environment_id',
29
+ ];
30
+ function record(value) {
31
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
32
+ }
33
+ function pick(source, keys) {
34
+ const safe = {};
35
+ for (const key of keys) {
36
+ if (source[key] !== undefined)
37
+ safe[key] = source[key];
38
+ }
39
+ return safe;
40
+ }
41
+ /**
42
+ * Rebuild the backend response from an allow-list. Even if a future backend regression adds a
43
+ * plaintext field, blind commands cannot return it through oclif's JSON formatter.
44
+ */
45
+ function safeTokenProvisionEnvelope(response) {
46
+ const data = record(response.data);
47
+ const safeData = data.provision === undefined
48
+ ? pick(data, provisionKeys)
49
+ : {
50
+ token: pick(record(data.token), tokenKeys),
51
+ provision: pick(record(data.provision), provisionKeys),
52
+ };
53
+ return response.meta ? { data: safeData, meta: response.meta } : { data: safeData };
54
+ }