@hyperdrive.bot/cli 1.0.13 → 1.0.17
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 +4526 -780
- package/dist/commands/deploy.d.ts +18 -0
- package/dist/commands/deploy.js +239 -0
- package/dist/commands/deployment/create.js +10 -2
- package/dist/commands/domain/{switch.d.ts → set-production.d.ts} +1 -1
- package/dist/commands/domain/set-production.js +27 -0
- package/dist/commands/git/list-open-prs.d.ts +12 -0
- package/dist/commands/git/list-open-prs.js +87 -0
- package/dist/commands/hook/add.d.ts +22 -0
- package/dist/commands/hook/add.js +299 -0
- package/dist/commands/hook/list.d.ts +11 -0
- package/dist/commands/hook/list.js +111 -0
- package/dist/commands/hook/logs.d.ts +13 -0
- package/dist/commands/hook/logs.js +124 -0
- package/dist/commands/hook/remove.d.ts +12 -0
- package/dist/commands/hook/remove.js +115 -0
- package/dist/commands/hook/toggle.d.ts +12 -0
- package/dist/commands/hook/toggle.js +125 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +49 -9
- package/dist/commands/module/bindings.d.ts +14 -0
- package/dist/commands/module/bindings.js +125 -0
- package/dist/commands/module/create.d.ts +3 -0
- package/dist/commands/module/create.js +156 -78
- package/dist/commands/module/list.d.ts +1 -0
- package/dist/commands/module/list.js +22 -1
- package/dist/commands/module/sync.d.ts +29 -0
- package/dist/commands/module/sync.js +409 -0
- package/dist/commands/module/unlink.d.ts +11 -0
- package/dist/commands/module/unlink.js +77 -0
- package/dist/commands/module/update.d.ts +10 -0
- package/dist/commands/module/update.js +168 -5
- package/dist/commands/network/discover.d.ts +12 -0
- package/dist/commands/network/discover.js +210 -0
- package/dist/commands/network/get.d.ts +13 -0
- package/dist/commands/network/get.js +90 -0
- package/dist/commands/{auth/logout.d.ts → network/list.d.ts} +2 -9
- package/dist/commands/network/list.js +71 -0
- package/dist/commands/network/register.d.ts +16 -0
- package/dist/commands/network/register.js +144 -0
- package/dist/commands/parameter/sync.d.ts +13 -0
- package/dist/commands/parameter/sync.js +69 -1
- package/dist/commands/project/sync.d.ts +5 -11
- package/dist/commands/project/sync.js +12 -381
- package/dist/commands/seed.d.ts +93 -0
- package/dist/commands/seed.js +324 -0
- package/dist/commands/service/backup.d.ts +17 -0
- package/dist/commands/service/backup.js +156 -0
- package/dist/commands/service/backups.d.ts +14 -0
- package/dist/commands/service/backups.js +110 -0
- package/dist/commands/service/bind.d.ts +16 -0
- package/dist/commands/service/bind.js +106 -0
- package/dist/commands/service/bindings.d.ts +13 -0
- package/dist/commands/service/bindings.js +78 -0
- package/dist/commands/service/clone.d.ts +19 -0
- package/dist/commands/service/clone.js +153 -0
- package/dist/commands/service/create.d.ts +16 -0
- package/dist/commands/service/create.js +212 -0
- package/dist/commands/service/get.d.ts +13 -0
- package/dist/commands/service/get.js +97 -0
- package/dist/commands/service/list.d.ts +12 -0
- package/dist/commands/service/list.js +86 -0
- package/dist/commands/service/register.d.ts +21 -0
- package/dist/commands/service/register.js +215 -0
- package/dist/commands/service/restore.d.ts +19 -0
- package/dist/commands/service/restore.js +158 -0
- package/dist/commands/service/seed.d.ts +17 -0
- package/dist/commands/service/seed.js +173 -0
- package/dist/commands/service/templates.d.ts +10 -0
- package/dist/commands/service/templates.js +66 -0
- package/dist/commands/service/unbind.d.ts +15 -0
- package/dist/commands/service/unbind.js +74 -0
- package/dist/commands/stage/create.d.ts +23 -0
- package/dist/commands/stage/create.js +145 -6
- package/dist/commands/stage/delete.d.ts +11 -0
- package/dist/commands/stage/delete.js +85 -0
- package/dist/commands/stage/deploy.d.ts +34 -0
- package/dist/commands/stage/deploy.js +294 -0
- package/dist/commands/stage/ensure-branches.d.ts +23 -0
- package/dist/commands/stage/ensure-branches.js +101 -0
- package/dist/commands/stage/list.js +4 -0
- package/dist/commands/stage/status.d.ts +14 -0
- package/dist/commands/stage/status.js +100 -0
- package/dist/commands/{jira → tracker}/connect.js +32 -23
- package/dist/commands/tracker/hook/add.d.ts +25 -0
- package/dist/commands/tracker/hook/add.js +284 -0
- package/dist/commands/{jira → tracker}/hook/list.js +20 -11
- package/dist/commands/{jira/hook/add.d.ts → tracker/hook/logs.d.ts} +2 -3
- package/dist/commands/tracker/hook/logs.js +126 -0
- package/dist/commands/{jira → tracker}/hook/remove.js +9 -8
- package/dist/commands/{jira → tracker}/hook/toggle.js +14 -12
- package/dist/commands/tracker/project/init.d.ts +17 -0
- package/dist/commands/tracker/project/init.js +178 -0
- package/dist/commands/tracker/project/link-module.d.ts +17 -0
- package/dist/commands/tracker/project/link-module.js +287 -0
- package/dist/commands/tracker/project/list-modules.d.ts +11 -0
- package/dist/commands/tracker/project/list-modules.js +117 -0
- package/dist/commands/tracker/project/list.d.ts +10 -0
- package/dist/commands/tracker/project/list.js +90 -0
- package/dist/commands/tracker/project/status.d.ts +13 -0
- package/dist/commands/tracker/project/status.js +168 -0
- package/dist/commands/tracker/project/unlink-module.d.ts +13 -0
- package/dist/commands/tracker/project/unlink-module.js +251 -0
- package/dist/commands/{jira → tracker}/status.js +3 -3
- package/dist/lib/ensure-branches.d.ts +53 -0
- package/dist/lib/ensure-branches.js +149 -0
- package/dist/lib/git-providers/github.d.ts +16 -0
- package/dist/lib/git-providers/github.js +157 -0
- package/dist/lib/git-providers/gitlab.d.ts +16 -0
- package/dist/lib/git-providers/gitlab.js +148 -0
- package/dist/lib/git-providers/index.d.ts +67 -0
- package/dist/lib/git-providers/index.js +39 -0
- package/dist/lib/lambda-warmer.d.ts +106 -0
- package/dist/lib/lambda-warmer.js +189 -0
- package/dist/services/hyperdrive-sigv4.d.ts +359 -5
- package/dist/services/hyperdrive-sigv4.js +177 -12
- package/dist/utils/hook-flow.d.ts +60 -3
- package/dist/utils/hook-flow.js +437 -2
- package/dist/utils/hook-normalize.d.ts +6 -0
- package/dist/utils/hook-normalize.js +33 -0
- package/dist/utils/lifecycle-poller.d.ts +32 -0
- package/dist/utils/lifecycle-poller.js +72 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.js +88 -0
- package/dist/utils/summary-display.js +1 -1
- package/dist/utils/tracker-project-flow.d.ts +84 -0
- package/dist/utils/tracker-project-flow.js +564 -0
- package/package.json +41 -13
- package/dist/commands/auth/login.d.ts +0 -16
- package/dist/commands/auth/login.js +0 -179
- package/dist/commands/auth/logout.js +0 -116
- package/dist/commands/auth/refresh.d.ts +0 -6
- package/dist/commands/auth/refresh.js +0 -66
- package/dist/commands/auth/status.d.ts +0 -6
- package/dist/commands/auth/status.js +0 -63
- package/dist/commands/config/get.d.ts +0 -9
- package/dist/commands/config/get.js +0 -37
- package/dist/commands/config/set.d.ts +0 -10
- package/dist/commands/config/set.js +0 -48
- package/dist/commands/config/show.d.ts +0 -6
- package/dist/commands/config/show.js +0 -10
- package/dist/commands/domain/current.d.ts +0 -6
- package/dist/commands/domain/current.js +0 -18
- package/dist/commands/domain/list.d.ts +0 -6
- package/dist/commands/domain/list.js +0 -42
- package/dist/commands/domain/switch.js +0 -40
- package/dist/commands/jira/hook/add.js +0 -147
- package/dist/services/tenant-service.d.ts +0 -127
- package/dist/services/tenant-service.js +0 -396
- package/dist/utils/auth-flow.d.ts +0 -147
- package/dist/utils/auth-flow.js +0 -479
- package/oclif.manifest.json +0 -3519
- /package/dist/commands/{jira → tracker}/connect.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/list.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/remove.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/toggle.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/status.d.ts +0 -0
|
@@ -4,7 +4,7 @@ import inquirer from 'inquirer';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
|
|
6
6
|
export default class JiraConnect extends Command {
|
|
7
|
-
static description = '
|
|
7
|
+
static description = 'Connect your tenant to a Jira instance via the Hyperdrive Forge app';
|
|
8
8
|
static examples = [
|
|
9
9
|
'<%= config.bin %> <%= command.id %>',
|
|
10
10
|
'<%= config.bin %> <%= command.id %> --jira-domain dev-squad.atlassian.net',
|
|
@@ -25,8 +25,8 @@ export default class JiraConnect extends Command {
|
|
|
25
25
|
this.log('');
|
|
26
26
|
this.log(chalk.blue.bold('🔌 Hyperdrive Jira Integration Setup'));
|
|
27
27
|
this.log('');
|
|
28
|
-
this.log(chalk.dim('This
|
|
29
|
-
this.log(chalk.dim('
|
|
28
|
+
this.log(chalk.dim('This will request a connection between your tenant and a Jira site.'));
|
|
29
|
+
this.log(chalk.dim('A Jira admin must approve the connection from the Forge app settings.'));
|
|
30
30
|
this.log('');
|
|
31
31
|
// Create API service (automatically checks auth)
|
|
32
32
|
let apiService;
|
|
@@ -82,38 +82,47 @@ export default class JiraConnect extends Command {
|
|
|
82
82
|
// Pre-register domain
|
|
83
83
|
spinner.start('Registering Jira domain with Hyperdrive...');
|
|
84
84
|
try {
|
|
85
|
-
const response = await apiService
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
const response = await apiService.jiraPreRegister({ jiraDomain: jiraDomain });
|
|
86
|
+
// Handle already connected case
|
|
87
|
+
if (response.alreadyConnected) {
|
|
88
|
+
spinner.succeed('Jira instance already connected');
|
|
89
|
+
this.log('');
|
|
90
|
+
this.log(chalk.green('✅ Already Connected!'));
|
|
91
|
+
this.log('');
|
|
92
|
+
this.log(` Jira Domain: ${chalk.cyan(response.connection?.jiraDomain)}`);
|
|
93
|
+
this.log(` Status: ${chalk.green('active')}`);
|
|
94
|
+
this.log(` Connected since: ${chalk.dim(response.connection?.installedAt ? new Date(response.connection.installedAt).toLocaleString() : 'unknown')}`);
|
|
95
|
+
this.log('');
|
|
96
|
+
this.log(chalk.dim('Your Jira integration is already active. No action needed.'));
|
|
97
|
+
this.log(chalk.dim(`Run ${chalk.cyan('hd tracker status')} to see full connection details.`));
|
|
98
|
+
this.log('');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
spinner.succeed('Connection request submitted');
|
|
90
102
|
this.log('');
|
|
91
|
-
this.log(chalk.green('
|
|
103
|
+
this.log(chalk.green('Connection Request Submitted!'));
|
|
92
104
|
this.log('');
|
|
93
|
-
this.log(chalk.bold('
|
|
94
|
-
this.log(` Jira Domain: ${chalk.cyan(response.registration
|
|
95
|
-
this.log(` Tenant ID: ${chalk.dim(response.registration
|
|
96
|
-
this.log(`
|
|
97
|
-
this.log(` Status: ${chalk.yellow(response.registration.status)}`);
|
|
105
|
+
this.log(chalk.bold('Request Details:'));
|
|
106
|
+
this.log(` Jira Domain: ${chalk.cyan(response.registration?.jiraDomain)}`);
|
|
107
|
+
this.log(` Tenant ID: ${chalk.dim(response.registration?.tenantId)}`);
|
|
108
|
+
this.log(` Status: ${chalk.yellow('pending approval')}`);
|
|
98
109
|
this.log('');
|
|
99
|
-
// Show install link
|
|
110
|
+
// Show install link if app not yet installed on the Jira site
|
|
100
111
|
if (response.nextSteps.forgeInstallUrl) {
|
|
101
|
-
this.log(chalk.bold('Install the Forge App:'));
|
|
112
|
+
this.log(chalk.bold('Step 1 - Install the Forge App (if not already installed):'));
|
|
102
113
|
this.log('');
|
|
103
114
|
this.log(` ${chalk.cyan.underline(response.nextSteps.forgeInstallUrl)}`);
|
|
104
115
|
this.log('');
|
|
105
116
|
this.log(chalk.dim(' Open this link in your browser, select your Jira site, and approve the permissions.'));
|
|
106
117
|
this.log('');
|
|
107
118
|
}
|
|
108
|
-
this.log(chalk.bold('
|
|
109
|
-
response.nextSteps.instructions.forEach((instruction, index) => {
|
|
110
|
-
this.log(` ${chalk.cyan(`${index + 1}.`)} ${instruction}`);
|
|
111
|
-
});
|
|
119
|
+
this.log(chalk.bold(`${response.nextSteps.forgeInstallUrl ? 'Step 2' : 'Next Step'} - Jira Admin Approval:`));
|
|
112
120
|
this.log('');
|
|
113
|
-
this.log(
|
|
114
|
-
this.log(
|
|
121
|
+
this.log(' A Jira administrator must approve your connection request.');
|
|
122
|
+
this.log(` They can do this from: ${chalk.cyan('Jira Settings > Apps > Hyperdrive > Configure')}`);
|
|
123
|
+
this.log(` Your tenant will appear under ${chalk.bold('Pending Connection Requests')}.`);
|
|
115
124
|
this.log('');
|
|
116
|
-
this.log(chalk.
|
|
125
|
+
this.log(chalk.dim(`Run ${chalk.cyan('hd tracker status')} to check the connection status.`));
|
|
117
126
|
this.log('');
|
|
118
127
|
}
|
|
119
128
|
catch (error) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class HookAdd extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
project: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
'action-config': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'action-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'trigger-conditions': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'trigger-event': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
order: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
};
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
private handleApiError;
|
|
22
|
+
private runLegacyFlow;
|
|
23
|
+
private runV2Interactive;
|
|
24
|
+
private runV2NonInteractive;
|
|
25
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { HyperdriveSigV4Service } from '../../../services/hyperdrive-sigv4.js';
|
|
5
|
+
import { ALL_ACTION_TYPES, VALID_TRIGGER_EVENTS, getActionCategory, promptActionConfig, promptActionTypeV2, promptOrder, promptTriggerConditions, promptTriggerEvent, } from '../../../utils/hook-flow.js';
|
|
6
|
+
const LEGACY_ACTION_TYPES = ['adhb-enrich', 'ci-trigger', 'slack-notify', 'webhook'];
|
|
7
|
+
export default class HookAdd extends Command {
|
|
8
|
+
static args = {
|
|
9
|
+
project: Args.string({ description: 'Tracker project ID', required: true }),
|
|
10
|
+
};
|
|
11
|
+
static description = 'Add an automation hook to a tracker project';
|
|
12
|
+
static examples = [
|
|
13
|
+
'<%= config.bin %> tracker hook add my-project',
|
|
14
|
+
'<%= config.bin %> tracker hook add my-project --trigger-event status_transition --trigger-conditions \'{"statusTo":"In Progress"}\' --action-type git-create-branch --action-config \'{"branchPattern":"{issueKey}/{summary-slug}"}\'',
|
|
15
|
+
'<%= config.bin %> tracker hook add my-project --trigger-event issue_created --action-type slack-notify --action-config \'{"channel":"#tickets"}\'',
|
|
16
|
+
'<%= config.bin %> tracker hook add my-project --trigger-event status_transition --action-type git-create-mr --action-config \'{"draft":true}\' --order 2',
|
|
17
|
+
'<%= config.bin %> tracker hook add my-project --status "In Progress" --action adhb-enrich --config \'{"priority":"high"}\'',
|
|
18
|
+
'<%= config.bin %> tracker hook add my-project --json',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
// V2 flags
|
|
22
|
+
'action-config': Flags.string({
|
|
23
|
+
description: '[V2] Action config as JSON string',
|
|
24
|
+
}),
|
|
25
|
+
'action-type': Flags.string({
|
|
26
|
+
description: '[V2] Action type (git-create-branch, git-create-mr, git-delete-branch, slack-notify, adhb-enrich, webhook, ci-trigger)',
|
|
27
|
+
}),
|
|
28
|
+
'trigger-conditions': Flags.string({
|
|
29
|
+
description: '[V2] Trigger conditions as JSON string',
|
|
30
|
+
}),
|
|
31
|
+
'trigger-event': Flags.string({
|
|
32
|
+
description: '[V2] Trigger event (status_transition, issue_created, issue_assigned, field_changed, comment_added, label_changed)',
|
|
33
|
+
}),
|
|
34
|
+
// Legacy flags
|
|
35
|
+
action: Flags.string({
|
|
36
|
+
description: '[Legacy] Action type (slack-notify, adhb-enrich, webhook, ci-trigger)',
|
|
37
|
+
}),
|
|
38
|
+
config: Flags.string({
|
|
39
|
+
description: '[Legacy] Action config as JSON string',
|
|
40
|
+
}),
|
|
41
|
+
domain: Flags.string({
|
|
42
|
+
char: 'd',
|
|
43
|
+
description: 'Hyperdrive tenant domain',
|
|
44
|
+
}),
|
|
45
|
+
json: Flags.boolean({
|
|
46
|
+
description: 'Output raw JSON',
|
|
47
|
+
}),
|
|
48
|
+
order: Flags.integer({
|
|
49
|
+
description: 'Execution order (lower runs first)',
|
|
50
|
+
}),
|
|
51
|
+
status: Flags.string({
|
|
52
|
+
description: '[Legacy] Trigger status (Jira status name or "*" for all)',
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
async run() {
|
|
56
|
+
const { args, flags } = await this.parse(HookAdd);
|
|
57
|
+
const isJson = flags.json;
|
|
58
|
+
// Authenticate
|
|
59
|
+
let apiService;
|
|
60
|
+
const spinner = isJson ? null : ora('Checking authentication...').start();
|
|
61
|
+
try {
|
|
62
|
+
apiService = new HyperdriveSigV4Service(flags.domain);
|
|
63
|
+
spinner?.succeed('Authenticated');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
spinner?.fail('Not authenticated');
|
|
67
|
+
this.error(`${error.message}\n\n` +
|
|
68
|
+
`Please authenticate first with: ${chalk.cyan('hd auth login')}`);
|
|
69
|
+
}
|
|
70
|
+
// Detect which flag set is in use
|
|
71
|
+
const hasLegacyFlags = flags.status || flags.action || flags.config;
|
|
72
|
+
const hasV2Flags = flags['trigger-event'] || flags['action-type'] || flags['trigger-conditions'] || flags['action-config'];
|
|
73
|
+
// Error if both flag sets are mixed
|
|
74
|
+
if (hasLegacyFlags && hasV2Flags) {
|
|
75
|
+
this.error('Cannot mix legacy flags (--status, --action, --config) with V2 flags (--trigger-event, --action-type, --trigger-conditions, --action-config).\n' +
|
|
76
|
+
'Use one set or the other.');
|
|
77
|
+
}
|
|
78
|
+
if (hasLegacyFlags) {
|
|
79
|
+
await this.runLegacyFlow(apiService, args.project, flags, isJson);
|
|
80
|
+
}
|
|
81
|
+
else if (hasV2Flags) {
|
|
82
|
+
await this.runV2NonInteractive(apiService, args.project, flags, isJson);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// No flags — interactive V2 flow
|
|
86
|
+
if (isJson) {
|
|
87
|
+
this.error('Interactive mode not supported with --json. Provide --trigger-event and --action-type flags.');
|
|
88
|
+
}
|
|
89
|
+
await this.runV2Interactive(apiService, args.project, isJson);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
handleApiError(error) {
|
|
93
|
+
let errorMessage = error.message;
|
|
94
|
+
if (error.response) {
|
|
95
|
+
const status = error.response.status;
|
|
96
|
+
const data = error.response.data;
|
|
97
|
+
if (status === 401) {
|
|
98
|
+
errorMessage = 'Authentication failed — please run "hd auth login"';
|
|
99
|
+
}
|
|
100
|
+
else if (status === 403) {
|
|
101
|
+
errorMessage = 'Access denied — check your permissions';
|
|
102
|
+
}
|
|
103
|
+
else if (status === 404) {
|
|
104
|
+
errorMessage = 'Project not found — verify the project ID';
|
|
105
|
+
}
|
|
106
|
+
else if (data?.error) {
|
|
107
|
+
errorMessage = data.error;
|
|
108
|
+
}
|
|
109
|
+
else if (data?.message) {
|
|
110
|
+
errorMessage = data.message;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.error(errorMessage);
|
|
114
|
+
}
|
|
115
|
+
async runLegacyFlow(apiService, projectId, flags, isJson) {
|
|
116
|
+
const hasAllFlags = flags.status && flags.action && flags.config;
|
|
117
|
+
if (!hasAllFlags) {
|
|
118
|
+
this.error('Non-interactive legacy mode requires all three flags: --status, --action, and --config\n' +
|
|
119
|
+
`Missing: ${[!flags.status && '--status', !flags.action && '--action', !flags.config && '--config'].filter(Boolean).join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
const triggerStatus = flags.status;
|
|
122
|
+
if (!LEGACY_ACTION_TYPES.includes(flags.action)) {
|
|
123
|
+
this.error(`Invalid action type: ${flags.action}. Must be one of: ${LEGACY_ACTION_TYPES.join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
const actionType = flags.action;
|
|
126
|
+
let actionConfig;
|
|
127
|
+
try {
|
|
128
|
+
actionConfig = JSON.parse(flags.config);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
this.error('Invalid JSON in --config flag');
|
|
132
|
+
}
|
|
133
|
+
const createSpinner = isJson ? null : ora('Creating hook...').start();
|
|
134
|
+
try {
|
|
135
|
+
const body = { actionConfig, actionType, triggerStatus };
|
|
136
|
+
const hook = await apiService.trackerProjectHookCreate(projectId, body);
|
|
137
|
+
createSpinner?.succeed('Hook created');
|
|
138
|
+
if (isJson) {
|
|
139
|
+
this.log(JSON.stringify(hook, null, 2));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.log('');
|
|
143
|
+
this.log(chalk.green('Hook created successfully'));
|
|
144
|
+
this.log('');
|
|
145
|
+
this.log(` Hook ID: ${chalk.cyan(hook.hookId)}`);
|
|
146
|
+
this.log(` Trigger Status: ${chalk.cyan(hook.triggerStatus)}`);
|
|
147
|
+
this.log(` Action Type: ${chalk.cyan(hook.actionType)}`);
|
|
148
|
+
this.log(` Enabled: ${hook.enabled ? chalk.green('yes') : chalk.red('no')}`);
|
|
149
|
+
this.log(` Created: ${chalk.dim(hook.createdAt)}`);
|
|
150
|
+
this.log('');
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
createSpinner?.fail('Failed to create hook');
|
|
154
|
+
this.handleApiError(error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async runV2Interactive(apiService, projectId, isJson) {
|
|
158
|
+
this.log('');
|
|
159
|
+
// Step 1: Trigger event
|
|
160
|
+
const triggerEvent = await promptTriggerEvent();
|
|
161
|
+
// Step 2: Trigger conditions
|
|
162
|
+
const conditions = await promptTriggerConditions(triggerEvent);
|
|
163
|
+
// Step 3: Action type (grouped)
|
|
164
|
+
const { category, type: actionType } = await promptActionTypeV2();
|
|
165
|
+
// Step 4: Action config
|
|
166
|
+
const actionConfig = await promptActionConfig(actionType);
|
|
167
|
+
// Step 5: Order
|
|
168
|
+
const order = await promptOrder();
|
|
169
|
+
// Create hook
|
|
170
|
+
const createSpinner = ora('Creating hook...').start();
|
|
171
|
+
try {
|
|
172
|
+
const body = {
|
|
173
|
+
action: { category, config: actionConfig, type: actionType },
|
|
174
|
+
trigger: {
|
|
175
|
+
conditions: Object.keys(conditions).length > 0 ? conditions : undefined,
|
|
176
|
+
event: triggerEvent,
|
|
177
|
+
source: 'jira',
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
if (order !== undefined)
|
|
181
|
+
body.order = order;
|
|
182
|
+
const hook = await apiService.trackerProjectHookCreateV2(projectId, body);
|
|
183
|
+
createSpinner.succeed('Hook created');
|
|
184
|
+
this.log('');
|
|
185
|
+
this.log(chalk.green('Hook created successfully'));
|
|
186
|
+
this.log('');
|
|
187
|
+
this.log(` Hook ID: ${chalk.cyan(hook.hookId)}`);
|
|
188
|
+
this.log(` Trigger Event: ${chalk.cyan(triggerEvent)}`);
|
|
189
|
+
if (Object.keys(conditions).length > 0) {
|
|
190
|
+
this.log(` Conditions: ${chalk.cyan(JSON.stringify(conditions))}`);
|
|
191
|
+
}
|
|
192
|
+
this.log(` Action Type: ${chalk.cyan(actionType)}`);
|
|
193
|
+
this.log(` Category: ${chalk.cyan(category)}`);
|
|
194
|
+
if (order !== undefined) {
|
|
195
|
+
this.log(` Order: ${chalk.cyan(String(order))}`);
|
|
196
|
+
}
|
|
197
|
+
this.log(` Enabled: ${hook.enabled ? chalk.green('yes') : chalk.red('no')}`);
|
|
198
|
+
this.log(` Created: ${chalk.dim(hook.createdAt)}`);
|
|
199
|
+
this.log('');
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
createSpinner.fail('Failed to create hook');
|
|
203
|
+
this.handleApiError(error);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async runV2NonInteractive(apiService, projectId, flags, isJson) {
|
|
207
|
+
// Require at minimum --trigger-event and --action-type
|
|
208
|
+
if (!flags['trigger-event'] || !flags['action-type']) {
|
|
209
|
+
this.error('V2 non-interactive mode requires at least --trigger-event and --action-type.\n' +
|
|
210
|
+
`Missing: ${[!flags['trigger-event'] && '--trigger-event', !flags['action-type'] && '--action-type'].filter(Boolean).join(', ')}`);
|
|
211
|
+
}
|
|
212
|
+
// Validate trigger event
|
|
213
|
+
const triggerEvent = flags['trigger-event'];
|
|
214
|
+
if (!VALID_TRIGGER_EVENTS.includes(triggerEvent)) {
|
|
215
|
+
this.error(`Invalid trigger event: ${triggerEvent}. Must be one of: ${VALID_TRIGGER_EVENTS.join(', ')}`);
|
|
216
|
+
}
|
|
217
|
+
// Validate action type
|
|
218
|
+
const actionType = flags['action-type'];
|
|
219
|
+
if (!ALL_ACTION_TYPES.includes(actionType)) {
|
|
220
|
+
this.error(`Invalid action type: ${actionType}. Must be one of: ${ALL_ACTION_TYPES.join(', ')}`);
|
|
221
|
+
}
|
|
222
|
+
// Parse optional conditions
|
|
223
|
+
let conditions;
|
|
224
|
+
if (flags['trigger-conditions']) {
|
|
225
|
+
try {
|
|
226
|
+
conditions = JSON.parse(flags['trigger-conditions']);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
this.error('Invalid JSON in --trigger-conditions flag');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Parse optional action config
|
|
233
|
+
let actionConfig = {};
|
|
234
|
+
if (flags['action-config']) {
|
|
235
|
+
try {
|
|
236
|
+
actionConfig = JSON.parse(flags['action-config']);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
this.error('Invalid JSON in --action-config flag');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const category = getActionCategory(actionType);
|
|
243
|
+
const order = flags.order;
|
|
244
|
+
const createSpinner = isJson ? null : ora('Creating hook...').start();
|
|
245
|
+
try {
|
|
246
|
+
const body = {
|
|
247
|
+
action: { category, config: actionConfig, type: actionType },
|
|
248
|
+
trigger: {
|
|
249
|
+
conditions,
|
|
250
|
+
event: triggerEvent,
|
|
251
|
+
source: 'jira',
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
if (order !== undefined)
|
|
255
|
+
body.order = order;
|
|
256
|
+
const hook = await apiService.trackerProjectHookCreateV2(projectId, body);
|
|
257
|
+
createSpinner?.succeed('Hook created');
|
|
258
|
+
if (isJson) {
|
|
259
|
+
this.log(JSON.stringify(hook, null, 2));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this.log('');
|
|
263
|
+
this.log(chalk.green('Hook created successfully'));
|
|
264
|
+
this.log('');
|
|
265
|
+
this.log(` Hook ID: ${chalk.cyan(hook.hookId)}`);
|
|
266
|
+
this.log(` Trigger Event: ${chalk.cyan(triggerEvent)}`);
|
|
267
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
268
|
+
this.log(` Conditions: ${chalk.cyan(JSON.stringify(conditions))}`);
|
|
269
|
+
}
|
|
270
|
+
this.log(` Action Type: ${chalk.cyan(actionType)}`);
|
|
271
|
+
this.log(` Category: ${chalk.cyan(category)}`);
|
|
272
|
+
if (order !== undefined) {
|
|
273
|
+
this.log(` Order: ${chalk.cyan(String(order))}`);
|
|
274
|
+
}
|
|
275
|
+
this.log(` Enabled: ${hook.enabled ? chalk.green('yes') : chalk.red('no')}`);
|
|
276
|
+
this.log(` Created: ${chalk.dim(hook.createdAt)}`);
|
|
277
|
+
this.log('');
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
createSpinner?.fail('Failed to create hook');
|
|
281
|
+
this.handleApiError(error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -2,15 +2,21 @@ import { Args, Command, Flags } from '@oclif/core';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { HyperdriveSigV4Service } from '../../../services/hyperdrive-sigv4.js';
|
|
5
|
+
import { normalizeHookToV2 } from '../../../utils/hook-normalize.js';
|
|
5
6
|
import { printTable } from '../../../utils/table.js';
|
|
7
|
+
function formatConditions(conditions) {
|
|
8
|
+
if (!conditions || Object.keys(conditions).length === 0)
|
|
9
|
+
return '*';
|
|
10
|
+
return Object.entries(conditions).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
11
|
+
}
|
|
6
12
|
export default class HookList extends Command {
|
|
7
13
|
static args = {
|
|
8
|
-
project: Args.string({ description: '
|
|
14
|
+
project: Args.string({ description: 'Tracker project ID', required: true }),
|
|
9
15
|
};
|
|
10
|
-
static description = 'List
|
|
16
|
+
static description = 'List automation hooks for a tracker project';
|
|
11
17
|
static examples = [
|
|
12
|
-
'<%= config.bin %>
|
|
13
|
-
'<%= config.bin %>
|
|
18
|
+
'<%= config.bin %> tracker hook list my-project',
|
|
19
|
+
'<%= config.bin %> tracker hook list my-project --json',
|
|
14
20
|
];
|
|
15
21
|
static flags = {
|
|
16
22
|
domain: Flags.string({
|
|
@@ -39,8 +45,9 @@ export default class HookList extends Command {
|
|
|
39
45
|
// Fetch hooks
|
|
40
46
|
const fetchSpinner = isJson ? null : ora('Fetching hooks...').start();
|
|
41
47
|
try {
|
|
42
|
-
const response = await apiService.
|
|
43
|
-
const
|
|
48
|
+
const response = await apiService.trackerProjectHookList(args.project);
|
|
49
|
+
const rawHooks = Array.isArray(response) ? response : response.hooks;
|
|
50
|
+
const hooks = rawHooks.map(normalizeHookToV2);
|
|
44
51
|
fetchSpinner?.succeed(`Found ${hooks.length} hook${hooks.length === 1 ? '' : 's'}`);
|
|
45
52
|
if (isJson) {
|
|
46
53
|
this.log(JSON.stringify(hooks, null, 2));
|
|
@@ -49,19 +56,21 @@ export default class HookList extends Command {
|
|
|
49
56
|
if (hooks.length === 0) {
|
|
50
57
|
this.log('');
|
|
51
58
|
this.log(chalk.yellow('No hooks configured for this project'));
|
|
52
|
-
this.log(chalk.dim(`Run ${chalk.cyan(`hd
|
|
59
|
+
this.log(chalk.dim(`Run ${chalk.cyan(`hd tracker hook add ${args.project}`)} to create one`));
|
|
53
60
|
this.log('');
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
63
|
this.log('');
|
|
57
|
-
printTable(hooks.map(h => ({
|
|
58
|
-
actionType: h.
|
|
64
|
+
printTable(hooks.map((h) => ({
|
|
65
|
+
actionType: h.action?.type ?? '',
|
|
66
|
+
conditions: formatConditions(h.trigger?.conditions),
|
|
59
67
|
createdAt: h.createdAt,
|
|
60
68
|
enabled: h.enabled,
|
|
61
69
|
hookId: h.hookId,
|
|
62
|
-
|
|
70
|
+
triggerEvent: h.trigger?.event ?? '',
|
|
63
71
|
})), {
|
|
64
|
-
|
|
72
|
+
triggerEvent: { header: 'Trigger Event' },
|
|
73
|
+
conditions: { header: 'Conditions' },
|
|
65
74
|
actionType: { header: 'Action Type' },
|
|
66
75
|
enabled: {
|
|
67
76
|
get: (row) => row.enabled ? chalk.green('enabled') : chalk.red('disabled'),
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
export default class HookLogs extends Command {
|
|
3
3
|
static args: {
|
|
4
4
|
project: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
-
action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
9
|
domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
10
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
12
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
13
|
};
|
|
15
14
|
run(): Promise<void>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { HyperdriveSigV4Service } from '../../../services/hyperdrive-sigv4.js';
|
|
5
|
+
import { printTable } from '../../../utils/table.js';
|
|
6
|
+
export default class HookLogs extends Command {
|
|
7
|
+
static args = {
|
|
8
|
+
project: Args.string({ description: 'Tracker project ID', required: true }),
|
|
9
|
+
};
|
|
10
|
+
static description = 'List recent hook execution logs for a tracker project';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> tracker hook logs my-project',
|
|
13
|
+
'<%= config.bin %> tracker hook logs my-project --status failed',
|
|
14
|
+
'<%= config.bin %> tracker hook logs my-project --limit 50',
|
|
15
|
+
'<%= config.bin %> tracker hook logs my-project --json',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
domain: Flags.string({
|
|
19
|
+
char: 'd',
|
|
20
|
+
description: 'Hyperdrive tenant domain',
|
|
21
|
+
}),
|
|
22
|
+
json: Flags.boolean({
|
|
23
|
+
description: 'Output raw JSON',
|
|
24
|
+
}),
|
|
25
|
+
limit: Flags.integer({
|
|
26
|
+
default: 20,
|
|
27
|
+
description: 'Number of log entries to display',
|
|
28
|
+
}),
|
|
29
|
+
status: Flags.string({
|
|
30
|
+
description: 'Filter by execution status',
|
|
31
|
+
options: ['success', 'failed'],
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const { args, flags } = await this.parse(HookLogs);
|
|
36
|
+
const isJson = flags.json;
|
|
37
|
+
// Authenticate
|
|
38
|
+
let apiService;
|
|
39
|
+
const spinner = isJson ? null : ora('Checking authentication...').start();
|
|
40
|
+
try {
|
|
41
|
+
apiService = new HyperdriveSigV4Service(flags.domain);
|
|
42
|
+
spinner?.succeed('Authenticated');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
spinner?.fail('Not authenticated');
|
|
46
|
+
this.error(`${error.message}\n\n` +
|
|
47
|
+
`Please authenticate first with: ${chalk.cyan('hd auth login')}`);
|
|
48
|
+
}
|
|
49
|
+
// Fetch hook logs
|
|
50
|
+
const fetchSpinner = isJson ? null : ora('Fetching hook logs...').start();
|
|
51
|
+
try {
|
|
52
|
+
const response = await apiService.trackerProjectHookLogList(args.project, {
|
|
53
|
+
limit: flags.limit,
|
|
54
|
+
status: flags.status,
|
|
55
|
+
});
|
|
56
|
+
const items = response.items;
|
|
57
|
+
fetchSpinner?.succeed(`Found ${items.length} log entr${items.length === 1 ? 'y' : 'ies'}`);
|
|
58
|
+
if (isJson) {
|
|
59
|
+
this.log(JSON.stringify(items, null, 2));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (items.length === 0) {
|
|
63
|
+
this.log('');
|
|
64
|
+
this.log(chalk.yellow('No hook executions found for this project. Hook logs appear after hooks are triggered.'));
|
|
65
|
+
this.log('');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.log('');
|
|
69
|
+
printTable(items.map(item => ({
|
|
70
|
+
actionType: item.actionType,
|
|
71
|
+
durationMs: item.durationMs,
|
|
72
|
+
hookId: item.hookId,
|
|
73
|
+
issueKey: item.issueKey,
|
|
74
|
+
status: item.status,
|
|
75
|
+
timestamp: item.timestamp,
|
|
76
|
+
})), {
|
|
77
|
+
timestamp: {
|
|
78
|
+
get: (row) => new Date(row.timestamp).toLocaleString(),
|
|
79
|
+
header: 'Timestamp',
|
|
80
|
+
},
|
|
81
|
+
hookId: {
|
|
82
|
+
get: (row) => row.hookId.slice(0, 8),
|
|
83
|
+
header: 'Hook ID',
|
|
84
|
+
},
|
|
85
|
+
actionType: { header: 'Action' },
|
|
86
|
+
issueKey: { header: 'Issue Key' },
|
|
87
|
+
status: {
|
|
88
|
+
get: (row) => row.status === 'success' ? chalk.green('success') : chalk.red('failed'),
|
|
89
|
+
header: 'Status',
|
|
90
|
+
},
|
|
91
|
+
durationMs: {
|
|
92
|
+
get: (row) => `${row.durationMs}ms`,
|
|
93
|
+
header: 'Duration',
|
|
94
|
+
},
|
|
95
|
+
}, (msg) => this.log(msg));
|
|
96
|
+
this.log('');
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
fetchSpinner?.fail('Failed to fetch hook logs');
|
|
100
|
+
this.handleApiError(error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
handleApiError(error) {
|
|
104
|
+
let errorMessage = error.message;
|
|
105
|
+
if (error.response) {
|
|
106
|
+
const status = error.response.status;
|
|
107
|
+
const data = error.response.data;
|
|
108
|
+
if (status === 401) {
|
|
109
|
+
errorMessage = 'Authentication failed — please run "hd auth login"';
|
|
110
|
+
}
|
|
111
|
+
else if (status === 403) {
|
|
112
|
+
errorMessage = 'Access denied — check your permissions';
|
|
113
|
+
}
|
|
114
|
+
else if (status === 404) {
|
|
115
|
+
errorMessage = 'Project not found — verify the project ID';
|
|
116
|
+
}
|
|
117
|
+
else if (data?.error) {
|
|
118
|
+
errorMessage = data.error;
|
|
119
|
+
}
|
|
120
|
+
else if (data?.message) {
|
|
121
|
+
errorMessage = data.message;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.error(errorMessage);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -3,15 +3,16 @@ import chalk from 'chalk';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { HyperdriveSigV4Service } from '../../../services/hyperdrive-sigv4.js';
|
|
5
5
|
import { promptConfirmDelete, promptSelectHook } from '../../../utils/hook-flow.js';
|
|
6
|
+
import { normalizeHookToV2 } from '../../../utils/hook-normalize.js';
|
|
6
7
|
export default class HookRemove extends Command {
|
|
7
8
|
static args = {
|
|
8
|
-
project: Args.string({ description: '
|
|
9
|
+
project: Args.string({ description: 'Tracker project ID', required: true }),
|
|
9
10
|
};
|
|
10
|
-
static description = 'Remove
|
|
11
|
+
static description = 'Remove an automation hook from a tracker project';
|
|
11
12
|
static examples = [
|
|
12
|
-
'<%= config.bin %>
|
|
13
|
-
'<%= config.bin %>
|
|
14
|
-
'<%= config.bin %>
|
|
13
|
+
'<%= config.bin %> tracker hook remove my-project',
|
|
14
|
+
'<%= config.bin %> tracker hook remove my-project --hook-id hook-123',
|
|
15
|
+
'<%= config.bin %> tracker hook remove my-project --hook-id hook-123 --json',
|
|
15
16
|
];
|
|
16
17
|
static flags = {
|
|
17
18
|
domain: Flags.string({
|
|
@@ -52,8 +53,8 @@ export default class HookRemove extends Command {
|
|
|
52
53
|
// Fetch hooks for interactive selection
|
|
53
54
|
const fetchSpinner = ora('Fetching hooks...').start();
|
|
54
55
|
try {
|
|
55
|
-
const response = await apiService.
|
|
56
|
-
const hooks = response.hooks;
|
|
56
|
+
const response = await apiService.trackerProjectHookList(args.project);
|
|
57
|
+
const hooks = (Array.isArray(response) ? response : response.hooks).map(normalizeHookToV2);
|
|
57
58
|
fetchSpinner.succeed(`Found ${hooks.length} hook${hooks.length === 1 ? '' : 's'}`);
|
|
58
59
|
if (hooks.length === 0) {
|
|
59
60
|
this.log('');
|
|
@@ -78,7 +79,7 @@ export default class HookRemove extends Command {
|
|
|
78
79
|
// Delete hook
|
|
79
80
|
const deleteSpinner = isJson ? null : ora('Deleting hook...').start();
|
|
80
81
|
try {
|
|
81
|
-
const result = await apiService.
|
|
82
|
+
const result = await apiService.trackerProjectHookDelete(args.project, hookId);
|
|
82
83
|
deleteSpinner?.succeed('Hook deleted');
|
|
83
84
|
if (isJson) {
|
|
84
85
|
this.log(JSON.stringify({ hookId, ...result }, null, 2));
|