deepline 0.3.293 → 0.3.295
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/bundling-sources/sdk/src/compat.ts +35 -7
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +269 -267
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +3 -4
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +14 -1
- package/dist/cli/auth-EDTRERZP.mjs +31 -0
- package/dist/cli/auth-TDHKB277.js +31 -0
- package/dist/cli/billing-ELIDPILY.js +1749 -0
- package/dist/cli/billing-EXGXJVAU.mjs +1749 -0
- package/dist/cli/chunk-2S5Y3TZP.js +5688 -0
- package/dist/cli/chunk-3KC45UVF.js +150 -0
- package/dist/cli/chunk-3XX5TEA4.js +15 -0
- package/dist/cli/chunk-4MBOJQPL.mjs +0 -0
- package/dist/cli/chunk-4NEFYBF7.js +2240 -0
- package/dist/cli/chunk-5JLQPVNX.js +3056 -0
- package/dist/cli/chunk-5KDKFHZI.js +1269 -0
- package/dist/cli/chunk-6A3TJUWC.js +76 -0
- package/dist/cli/chunk-6O5YZOVN.mjs +890 -0
- package/dist/cli/chunk-7P5AA3SN.js +344 -0
- package/dist/cli/chunk-AYHLUHE5.js +52 -0
- package/dist/cli/chunk-CBVIJSHF.mjs +560 -0
- package/dist/cli/chunk-CFVVCN4A.js +735 -0
- package/dist/cli/chunk-COGTRVWW.js +30 -0
- package/dist/cli/chunk-D4AZUZO4.js +190 -0
- package/dist/cli/chunk-DLIRSHV6.mjs +28 -0
- package/dist/cli/chunk-EACVZI6N.mjs +1995 -0
- package/dist/cli/chunk-EDWJY6T4.mjs +3056 -0
- package/dist/cli/chunk-EXMLR72R.mjs +0 -0
- package/dist/cli/chunk-F35VOIQL.js +890 -0
- package/dist/cli/chunk-GAMFK2ZP.mjs +148 -0
- package/dist/cli/chunk-GHRQQPW2.mjs +2240 -0
- package/dist/cli/chunk-GONRLVV3.js +3440 -0
- package/dist/cli/chunk-GVUJJJ5G.mjs +76 -0
- package/dist/cli/chunk-H7HIDGZ7.mjs +150 -0
- package/dist/cli/chunk-HUZXWOSK.mjs +503 -0
- package/dist/cli/chunk-I5YZLGMG.js +1 -0
- package/dist/cli/chunk-ITOFSXIU.mjs +15 -0
- package/dist/cli/chunk-J5NA2TTX.js +503 -0
- package/dist/cli/chunk-JCS5TPNY.mjs +3440 -0
- package/dist/cli/chunk-JNNOCMQY.js +47 -0
- package/dist/cli/chunk-KRD6H754.mjs +30 -0
- package/dist/cli/chunk-LA46MMTL.js +560 -0
- package/dist/cli/chunk-LDWT7RJK.mjs +190 -0
- package/dist/cli/chunk-M2V26OR5.js +499 -0
- package/dist/cli/chunk-MIMASXO4.mjs +389 -0
- package/dist/cli/chunk-MROPCKW2.mjs +190 -0
- package/dist/cli/chunk-NXDRDJ4K.js +389 -0
- package/dist/cli/chunk-NXGCO4JB.mjs +344 -0
- package/dist/cli/chunk-OG64RBAC.js +6 -0
- package/dist/cli/chunk-OQYEC4GL.mjs +34 -0
- package/dist/cli/chunk-OWJZUE3Y.mjs +103 -0
- package/dist/cli/chunk-SDK3ZA2C.js +103 -0
- package/dist/cli/chunk-SFLARO4Y.mjs +47 -0
- package/dist/cli/chunk-SVIIIYIQ.js +923 -0
- package/dist/cli/chunk-TAC3JIFE.mjs +354 -0
- package/dist/cli/chunk-U3PI7HBM.js +34 -0
- package/dist/cli/chunk-UFS752TU.mjs +52 -0
- package/dist/cli/chunk-UI37DQE7.mjs +499 -0
- package/dist/cli/chunk-UNEMDKXN.js +1 -0
- package/dist/cli/chunk-WB6Z2Q44.mjs +1622 -0
- package/dist/cli/chunk-WJYPBHTE.js +148 -0
- package/dist/cli/chunk-WOHBA5PV.js +354 -0
- package/dist/cli/chunk-WQICC2B3.js +190 -0
- package/dist/cli/chunk-WZYKRD7T.mjs +6 -0
- package/dist/cli/chunk-XL2FO6CW.mjs +5688 -0
- package/dist/cli/chunk-XNYIZMNQ.js +28 -0
- package/dist/cli/chunk-XRK2TZT5.js +1995 -0
- package/dist/cli/chunk-YJYPL5OK.mjs +1269 -0
- package/dist/cli/chunk-YWNDYCXR.mjs +735 -0
- package/dist/cli/chunk-YWWAF7KZ.mjs +923 -0
- package/dist/cli/chunk-ZDO3TO3M.js +1622 -0
- package/dist/cli/client-4JVOYVC5.js +20 -0
- package/dist/cli/client-EEN2IJN2.mjs +20 -0
- package/dist/cli/csv-BOHBEP2I.mjs +585 -0
- package/dist/cli/csv-S5NYRUEX.js +585 -0
- package/dist/cli/db-CLODWCOB.mjs +486 -0
- package/dist/cli/db-F6PKFOOV.js +486 -0
- package/dist/cli/executions-5TQAKLJU.js +28 -0
- package/dist/cli/executions-MAKW6QOY.mjs +28 -0
- package/dist/cli/feedback-GV4QJFEN.js +107 -0
- package/dist/cli/feedback-XY24TW7H.mjs +107 -0
- package/dist/cli/getting-started.js +18 -76
- package/dist/cli/getting-started.mjs +10 -37
- package/dist/cli/git-6S2C22T5.js +23 -0
- package/dist/cli/git-OZLBD5HH.mjs +23 -0
- package/dist/cli/index.js +291 -54294
- package/dist/cli/index.mjs +209 -54352
- package/dist/cli/legacy-noops-LLCGDPQE.js +157 -0
- package/dist/cli/legacy-noops-TYH3TW2D.mjs +157 -0
- package/dist/cli/monitors-CABMB4DW.js +3251 -0
- package/dist/cli/monitors-TTD55HMK.mjs +3251 -0
- package/dist/cli/org-MSF3RG76.js +849 -0
- package/dist/cli/org-SFJ77MWY.mjs +849 -0
- package/dist/cli/play-NXHJSCKL.mjs +18439 -0
- package/dist/cli/play-OKSFNNO4.js +18439 -0
- package/dist/cli/providers-QC2ERERL.js +95 -0
- package/dist/cli/providers-TIIAZEUA.mjs +95 -0
- package/dist/cli/proxy-env-EBANZHPV.js +161 -0
- package/dist/cli/proxy-env-M4ZHXHRL.mjs +161 -0
- package/dist/cli/quickstart-ICANHLMI.js +69 -0
- package/dist/cli/quickstart-KWUOP3FI.mjs +69 -0
- package/dist/cli/search-U5H2FDOO.mjs +18 -0
- package/dist/cli/search-XQRQOLWS.js +18 -0
- package/dist/cli/secrets-3J6YTWFA.mjs +400 -0
- package/dist/cli/secrets-YNMNZNOM.js +400 -0
- package/dist/cli/sessions-CLEES4L6.mjs +15 -0
- package/dist/cli/sessions-J6VR47FG.js +15 -0
- package/dist/cli/settings-EEKZMTY4.js +846 -0
- package/dist/cli/settings-UVLOQZRH.mjs +846 -0
- package/dist/cli/setup-DLMDXPGJ.mjs +1257 -0
- package/dist/cli/setup-HC4Q7QM4.js +1257 -0
- package/dist/cli/skills-33PHFBSB.mjs +28 -0
- package/dist/cli/skills-LFRY3KPX.js +28 -0
- package/dist/cli/tools-3COEPKML.mjs +2955 -0
- package/dist/cli/tools-QXEVCN45.js +2955 -0
- package/dist/cli/update-LSAC4FF5.mjs +28 -0
- package/dist/cli/update-NXHSPYXA.js +28 -0
- package/dist/cli/workflow-7S44OECF.mjs +1936 -0
- package/dist/cli/workflow-QPZWYSZE.js +1936 -0
- package/dist/helpers.js +6 -32
- package/dist/helpers.mjs +2 -0
- package/dist/index.js +1972 -12825
- package/dist/index.mjs +1910 -12672
- package/dist/install-integrity.json +110 -0
- package/dist/monitor-fleet-contract.js +31 -730
- package/dist/monitor-fleet-contract.mjs +18 -667
- package/dist/monitor-input-contract.js +10 -390
- package/dist/monitor-input-contract.mjs +7 -348
- package/dist/plays/bundle-play-file.mjs +13 -5178
- package/dist/plays/csv-data-input.js +5 -3132
- package/dist/plays/csv-data-input.mjs +5 -3105
- package/dist/plays/run-request.js +56 -664
- package/dist/plays/run-request.mjs +30 -588
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +5 -212
- package/dist/release.mjs +5 -185
- package/package.json +1 -1
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeeplineClient,
|
|
3
|
+
PRODUCT_NOTIFICATION_EVENT_CATALOG,
|
|
4
|
+
resolveLegacySlackNotification
|
|
5
|
+
} from "./chunk-XL2FO6CW.mjs";
|
|
6
|
+
import "./chunk-CBVIJSHF.mjs";
|
|
7
|
+
import {
|
|
8
|
+
readCliUpdatePreferences,
|
|
9
|
+
runUpdateCommand,
|
|
10
|
+
setCliAutoUpdateEnabled
|
|
11
|
+
} from "./chunk-YJYPL5OK.mjs";
|
|
12
|
+
import "./chunk-OQYEC4GL.mjs";
|
|
13
|
+
import "./chunk-GHRQQPW2.mjs";
|
|
14
|
+
import "./chunk-GAMFK2ZP.mjs";
|
|
15
|
+
import {
|
|
16
|
+
openInBrowser,
|
|
17
|
+
printCommandEnvelope
|
|
18
|
+
} from "./chunk-6O5YZOVN.mjs";
|
|
19
|
+
import "./chunk-EACVZI6N.mjs";
|
|
20
|
+
import "./chunk-DLIRSHV6.mjs";
|
|
21
|
+
import "./chunk-LDWT7RJK.mjs";
|
|
22
|
+
import "./chunk-GVUJJJ5G.mjs";
|
|
23
|
+
import "./chunk-NXGCO4JB.mjs";
|
|
24
|
+
import "./chunk-WZYKRD7T.mjs";
|
|
25
|
+
import "./chunk-KRD6H754.mjs";
|
|
26
|
+
|
|
27
|
+
// src/cli/commands/settings.ts
|
|
28
|
+
import { readFileSync } from "fs";
|
|
29
|
+
var eventHelp = PRODUCT_NOTIFICATION_EVENT_CATALOG.map(
|
|
30
|
+
(event) => ` ${event.id.padEnd(28)} ${event.description}`
|
|
31
|
+
).join("\n");
|
|
32
|
+
function slackDestination(settings) {
|
|
33
|
+
const destinations = settings.destinations.map(
|
|
34
|
+
(entry) => ({
|
|
35
|
+
...entry,
|
|
36
|
+
kind: typeof entry.kind === "string" ? entry.kind : "",
|
|
37
|
+
name: typeof entry.name === "string" ? entry.name : "",
|
|
38
|
+
archivedAt: typeof entry.archivedAt === "number" ? entry.archivedAt : void 0
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
return resolveLegacySlackNotification(destinations);
|
|
42
|
+
}
|
|
43
|
+
function eventLines(settings, destinationId) {
|
|
44
|
+
const enabled = new Map(
|
|
45
|
+
settings.subscriptions.filter(
|
|
46
|
+
(entry) => !destinationId || entry.destinationId === destinationId
|
|
47
|
+
).map((entry) => [entry.eventType, entry.enabled])
|
|
48
|
+
);
|
|
49
|
+
return settings.catalog.map(
|
|
50
|
+
(event) => `${event.id}: ${enabled.get(event.id) === true ? "enabled" : "disabled"} \u2014 ${event.description}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function requireCatalogEvent(settings, eventType) {
|
|
54
|
+
const event = settings.catalog.find((entry) => entry.id === eventType);
|
|
55
|
+
if (!event) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Unknown notification event "${eventType}". Run deepline settings notifications subscriptions list.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return event;
|
|
61
|
+
}
|
|
62
|
+
async function printSettings(options) {
|
|
63
|
+
const settings = await new DeeplineClient().getNotificationSettings();
|
|
64
|
+
const slack = slackDestination(settings);
|
|
65
|
+
const destinationId = typeof slack?._id === "string" ? slack._id : void 0;
|
|
66
|
+
printCommandEnvelope(
|
|
67
|
+
{
|
|
68
|
+
settings,
|
|
69
|
+
render: {
|
|
70
|
+
sections: [
|
|
71
|
+
{
|
|
72
|
+
title: "Slack destination",
|
|
73
|
+
lines: slack ? [
|
|
74
|
+
`status: ${String(slack.status ?? "unknown")}`,
|
|
75
|
+
`${slack.targetKind === "member" ? "member" : "channel"}: ${slack.targetKind === "member" ? "@" : "#"}${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
|
|
76
|
+
...slack.lastErrorMessage ? [`issue: ${String(slack.lastErrorMessage)}`] : []
|
|
77
|
+
] : ["Not configured."]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
title: "subscriptions",
|
|
81
|
+
lines: eventLines(settings, destinationId)
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: "dead letter queue",
|
|
85
|
+
lines: [
|
|
86
|
+
`${settings.dlq.count}${settings.dlq.capped ? "+" : ""} deliveries require review`
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
actions: slack ? [
|
|
91
|
+
{
|
|
92
|
+
label: "Validate Slack",
|
|
93
|
+
command: "deepline settings notifications test slack"
|
|
94
|
+
}
|
|
95
|
+
] : [
|
|
96
|
+
{
|
|
97
|
+
label: "Connect Slack",
|
|
98
|
+
command: "deepline settings notifications connect slack --open"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{ json: options.json }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
function registerSettingsCommands(program) {
|
|
107
|
+
const settings = program.command("settings").description("Manage Deepline settings.");
|
|
108
|
+
const autoupdate = settings.command("autoupdate").description("Configure automatic CLI updates on this machine.").addHelpText(
|
|
109
|
+
"after",
|
|
110
|
+
`
|
|
111
|
+
Examples:
|
|
112
|
+
deepline settings autoupdate
|
|
113
|
+
deepline settings autoupdate on
|
|
114
|
+
deepline settings autoupdate off
|
|
115
|
+
deepline settings autoupdate pin <version>
|
|
116
|
+
|
|
117
|
+
Automatic updates are enabled by default. Turning them on clears an existing
|
|
118
|
+
version pin. Pinning installs and holds one exact pin-aware CLI version.
|
|
119
|
+
`
|
|
120
|
+
).option("--json", "Emit JSON output").action((options) => {
|
|
121
|
+
const preferences = readCliUpdatePreferences();
|
|
122
|
+
printCommandEnvelope(
|
|
123
|
+
{
|
|
124
|
+
autoUpdateEnabled: preferences.autoUpdateEnabled,
|
|
125
|
+
pinnedVersion: preferences.pinnedVersion,
|
|
126
|
+
render: {
|
|
127
|
+
sections: [
|
|
128
|
+
{
|
|
129
|
+
title: "automatic CLI updates",
|
|
130
|
+
lines: [
|
|
131
|
+
`status: ${preferences.autoUpdateEnabled && !preferences.pinnedVersion ? "on" : "off"}`,
|
|
132
|
+
`pinned version: ${preferences.pinnedVersion ?? "none"}`
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{ json: options.json }
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
for (const enabled of [true, false]) {
|
|
142
|
+
autoupdate.command(enabled ? "on" : "off").description(
|
|
143
|
+
enabled ? "Enable automatic CLI updates and clear any version pin." : "Disable automatic CLI updates."
|
|
144
|
+
).option("--json", "Emit JSON output").action((options) => {
|
|
145
|
+
const preferences = setCliAutoUpdateEnabled(enabled);
|
|
146
|
+
printCommandEnvelope(
|
|
147
|
+
{
|
|
148
|
+
ok: true,
|
|
149
|
+
autoUpdateEnabled: preferences.autoUpdateEnabled,
|
|
150
|
+
pinnedVersion: preferences.pinnedVersion,
|
|
151
|
+
render: {
|
|
152
|
+
sections: [
|
|
153
|
+
{
|
|
154
|
+
title: "automatic CLI updates",
|
|
155
|
+
lines: [
|
|
156
|
+
enabled ? "enabled; any version pin was cleared" : "disabled"
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{ json: options.json }
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
autoupdate.command("pin").description("Install and hold an exact pin-aware CLI version.").argument("<version>", "Exact semantic version from a pin-aware release").option("--json", "Emit JSON output").action(async (version, options) => {
|
|
167
|
+
process.exitCode = await runUpdateCommand({
|
|
168
|
+
pin: version,
|
|
169
|
+
json: options.json,
|
|
170
|
+
executeWithJson: true
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
const notifications = settings.command("notifications").description(
|
|
174
|
+
"Configure Deepline product notifications and delivery safety."
|
|
175
|
+
).addHelpText(
|
|
176
|
+
"after",
|
|
177
|
+
`
|
|
178
|
+
Workflow:
|
|
179
|
+
1. deepline settings notifications connect slack --open
|
|
180
|
+
2. deepline settings notifications channels --query pipeline
|
|
181
|
+
3. deepline settings notifications set slack --channel '#pipeline-alerts'
|
|
182
|
+
4. deepline settings notifications test slack
|
|
183
|
+
5. deepline settings notifications subscriptions list
|
|
184
|
+
|
|
185
|
+
Slack is the delivery integration. This command group only manages product
|
|
186
|
+
notifications; there is no general integrations CLI.
|
|
187
|
+
`
|
|
188
|
+
);
|
|
189
|
+
notifications.command("get").description("Show destination, subscriptions, and DLQ health.").option("--json", "Emit JSON output").action(printSettings);
|
|
190
|
+
notifications.command("connect").description("Start the OAuth connection required by a destination.").argument("<destination>", "Destination kind (currently slack)").option("--open", "Open the Slack authorization URL in a browser").option("--dry-run", "Describe the mutation without starting OAuth").option("--json", "Emit JSON output").action(
|
|
191
|
+
async (destination, options) => {
|
|
192
|
+
if (destination !== "slack")
|
|
193
|
+
throw new Error("Only slack is supported.");
|
|
194
|
+
if (options.dryRun) {
|
|
195
|
+
printCommandEnvelope(
|
|
196
|
+
{ dryRun: true, destination, action: "start_oauth" },
|
|
197
|
+
{ json: options.json }
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const result = await new DeeplineClient().connectNotificationSlack();
|
|
202
|
+
const browser = options.open ? openInBrowser(result.redirect_url, { deduplicate: false }) : "not_requested";
|
|
203
|
+
printCommandEnvelope(
|
|
204
|
+
{
|
|
205
|
+
...result,
|
|
206
|
+
browser,
|
|
207
|
+
render: {
|
|
208
|
+
sections: [
|
|
209
|
+
{
|
|
210
|
+
title: "Slack authorization",
|
|
211
|
+
lines: [result.redirect_url, `browser: ${browser}`]
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
actions: [
|
|
215
|
+
{
|
|
216
|
+
label: "After authorization",
|
|
217
|
+
command: "deepline settings notifications set slack --channel '#channel'"
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{ json: options.json }
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
notifications.command("channels").description("List Slack channels visible to the connected Deepline app.").option("--query <text>", "Filter channels by name").option("--json", "Emit JSON output").action(async (options) => {
|
|
227
|
+
const result = await new DeeplineClient().listNotificationSlackChannels(
|
|
228
|
+
options.query
|
|
229
|
+
);
|
|
230
|
+
printCommandEnvelope(
|
|
231
|
+
{
|
|
232
|
+
...result,
|
|
233
|
+
render: {
|
|
234
|
+
sections: [
|
|
235
|
+
{
|
|
236
|
+
title: "Slack channels",
|
|
237
|
+
lines: result.channels.length ? result.channels.map(
|
|
238
|
+
(channel) => `#${channel.name} (${channel.id})${channel.isPrivate ? " private" : ""}`
|
|
239
|
+
) : ["No visible channels matched."]
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
{ json: options.json }
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").option("--channel <channel>", "Slack channel name or ID").option("--member-id <id>", "Slack member ID, for example U0123456789").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(
|
|
248
|
+
async (destination, options) => {
|
|
249
|
+
if (destination !== "slack")
|
|
250
|
+
throw new Error("Only slack is supported.");
|
|
251
|
+
if (Boolean(options.channel) === Boolean(options.memberId)) {
|
|
252
|
+
throw new Error("Provide exactly one of --channel or --member-id.");
|
|
253
|
+
}
|
|
254
|
+
const target = options.memberId ? { memberId: options.memberId } : options.channel;
|
|
255
|
+
if (options.dryRun) {
|
|
256
|
+
printCommandEnvelope(
|
|
257
|
+
{ dryRun: true, destination, target },
|
|
258
|
+
{ json: options.json }
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const result = await new DeeplineClient().setNotificationSlack(target);
|
|
263
|
+
printCommandEnvelope(
|
|
264
|
+
{
|
|
265
|
+
ok: true,
|
|
266
|
+
result,
|
|
267
|
+
render: {
|
|
268
|
+
sections: [
|
|
269
|
+
{
|
|
270
|
+
title: "Slack destination saved",
|
|
271
|
+
lines: [options.memberId ?? options.channel]
|
|
272
|
+
}
|
|
273
|
+
],
|
|
274
|
+
actions: [
|
|
275
|
+
{
|
|
276
|
+
label: "Validate delivery",
|
|
277
|
+
command: "deepline settings notifications test slack"
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
{ json: options.json }
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
notifications.command("test").description("Send a validation ping to a destination.").argument("<destination>", "Destination kind (currently slack)").option("--dry-run", "Describe the external send without sending").option("--json", "Emit JSON output").action(async (destination, options) => {
|
|
287
|
+
if (destination !== "slack") throw new Error("Only slack is supported.");
|
|
288
|
+
if (options.dryRun) {
|
|
289
|
+
printCommandEnvelope(
|
|
290
|
+
{ dryRun: true, destination, action: "send_test_ping" },
|
|
291
|
+
{ json: options.json }
|
|
292
|
+
);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const result = await new DeeplineClient().testNotificationSlack();
|
|
296
|
+
printCommandEnvelope(
|
|
297
|
+
{
|
|
298
|
+
...result,
|
|
299
|
+
render: {
|
|
300
|
+
sections: [{ title: "Slack validation", lines: [result.message] }]
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
{ json: options.json }
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
notifications.command("disable").description("Disable a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").option("--dry-run", "Describe the mutation without changing settings").option("--json", "Emit JSON output").action(async (destination, options) => {
|
|
307
|
+
if (destination !== "slack") throw new Error("Only slack is supported.");
|
|
308
|
+
if (options.dryRun) {
|
|
309
|
+
printCommandEnvelope(
|
|
310
|
+
{ dryRun: true, destination, action: "disable" },
|
|
311
|
+
{ json: options.json }
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const result = await new DeeplineClient().disableNotificationSlack();
|
|
316
|
+
printCommandEnvelope(
|
|
317
|
+
{
|
|
318
|
+
ok: true,
|
|
319
|
+
result,
|
|
320
|
+
render: {
|
|
321
|
+
sections: [{ title: "Slack destination", lines: ["disabled"] }]
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{ json: options.json }
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
const subscriptions = notifications.command("subscriptions").description("List and configure supported notification events.").addHelpText(
|
|
328
|
+
"after",
|
|
329
|
+
`
|
|
330
|
+
Supported events (shared product catalog):
|
|
331
|
+
${eventHelp}
|
|
332
|
+
`
|
|
333
|
+
);
|
|
334
|
+
subscriptions.command("list").description("List every supported event and its current state.").option("--json", "Emit JSON output").action(async (options) => {
|
|
335
|
+
const current = await new DeeplineClient().getNotificationSettings();
|
|
336
|
+
const destination = slackDestination(current);
|
|
337
|
+
const destinationId = typeof destination?._id === "string" ? destination._id : void 0;
|
|
338
|
+
printCommandEnvelope(
|
|
339
|
+
{
|
|
340
|
+
catalog: current.catalog,
|
|
341
|
+
subscriptions: current.subscriptions,
|
|
342
|
+
render: {
|
|
343
|
+
sections: [
|
|
344
|
+
{
|
|
345
|
+
title: "subscriptions",
|
|
346
|
+
lines: eventLines(current, destinationId)
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
{ json: options.json }
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
subscriptions.command("describe").description("Describe one event from the shared product catalog.").argument("<event>", "Event ID; run subscriptions list to enumerate").option("--json", "Emit JSON output").action(async (eventType, options) => {
|
|
355
|
+
const current = await new DeeplineClient().getNotificationSettings();
|
|
356
|
+
const event = requireCatalogEvent(current, eventType);
|
|
357
|
+
const destination = slackDestination(current);
|
|
358
|
+
const destinationId = typeof destination?._id === "string" ? destination._id : void 0;
|
|
359
|
+
const subscription = current.subscriptions.find(
|
|
360
|
+
(entry) => entry.eventType === event.id && (!destinationId || entry.destinationId === destinationId)
|
|
361
|
+
);
|
|
362
|
+
printCommandEnvelope(
|
|
363
|
+
{
|
|
364
|
+
event,
|
|
365
|
+
enabled: subscription?.enabled === true,
|
|
366
|
+
render: {
|
|
367
|
+
sections: [
|
|
368
|
+
{
|
|
369
|
+
title: event.id,
|
|
370
|
+
lines: [
|
|
371
|
+
event.description,
|
|
372
|
+
`enabled: ${subscription?.enabled === true}`
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{ json: options.json }
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
for (const enabled of [true, false]) {
|
|
382
|
+
subscriptions.command(enabled ? "enable" : "disable").description(`${enabled ? "Enable" : "Disable"} one supported event.`).argument("<event>", "Event ID; run subscriptions list to enumerate").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(async (eventType, options) => {
|
|
383
|
+
const client = new DeeplineClient();
|
|
384
|
+
const current = await client.getNotificationSettings();
|
|
385
|
+
const event = requireCatalogEvent(current, eventType);
|
|
386
|
+
if (options.dryRun) {
|
|
387
|
+
printCommandEnvelope(
|
|
388
|
+
{ dryRun: true, event, enabled },
|
|
389
|
+
{ json: options.json }
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const result = await client.setNotificationSubscriptions(
|
|
394
|
+
[event.id],
|
|
395
|
+
enabled
|
|
396
|
+
);
|
|
397
|
+
printCommandEnvelope(
|
|
398
|
+
{
|
|
399
|
+
ok: true,
|
|
400
|
+
event: event.id,
|
|
401
|
+
enabled,
|
|
402
|
+
result,
|
|
403
|
+
render: {
|
|
404
|
+
sections: [
|
|
405
|
+
{
|
|
406
|
+
title: "subscription updated",
|
|
407
|
+
lines: [`${event.id}: ${enabled ? "enabled" : "disabled"}`]
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
{ json: options.json }
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
const dlq = notifications.command("dlq").description("Inspect and explicitly resolve inert failed deliveries.");
|
|
417
|
+
dlq.command("list").option("--limit <count>", "Maximum deliveries", "25").option("--json", "Emit JSON output").action(async (options) => {
|
|
418
|
+
const result = await new DeeplineClient().listNotificationDlq(
|
|
419
|
+
Number(options.limit)
|
|
420
|
+
);
|
|
421
|
+
const deliveries = result.deliveries ?? [];
|
|
422
|
+
printCommandEnvelope(
|
|
423
|
+
{
|
|
424
|
+
...result,
|
|
425
|
+
render: {
|
|
426
|
+
sections: [
|
|
427
|
+
{
|
|
428
|
+
title: "dead letter queue",
|
|
429
|
+
lines: deliveries.length ? deliveries.map(
|
|
430
|
+
(entry) => `${String(entry._id)}: ${String(entry.lastErrorCode ?? "delivery_failed")}`
|
|
431
|
+
) : ["empty"]
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{ json: options.json }
|
|
437
|
+
);
|
|
438
|
+
});
|
|
439
|
+
dlq.command("get").argument("<delivery-id>").option("--json", "Emit JSON output").action(async (deliveryId, options) => {
|
|
440
|
+
const result = await new DeeplineClient().getNotificationDlqDelivery(
|
|
441
|
+
deliveryId
|
|
442
|
+
);
|
|
443
|
+
printCommandEnvelope(result, {
|
|
444
|
+
json: options.json
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
for (const action of ["retry", "archive"]) {
|
|
448
|
+
dlq.command(action).description(
|
|
449
|
+
`${action === "retry" ? "Replay once from attempt zero" : "Archive"} one delivery.`
|
|
450
|
+
).argument("<delivery-id>").option("--dry-run", "Describe the mutation without changing state").option("--json", "Emit JSON output").action(async (deliveryId, options) => {
|
|
451
|
+
if (options.dryRun) {
|
|
452
|
+
printCommandEnvelope(
|
|
453
|
+
{ dryRun: true, action, deliveryId },
|
|
454
|
+
{ json: options.json }
|
|
455
|
+
);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const result = await new DeeplineClient().updateNotificationDlqDelivery(
|
|
459
|
+
deliveryId,
|
|
460
|
+
action
|
|
461
|
+
);
|
|
462
|
+
printCommandEnvelope(
|
|
463
|
+
{ ok: true, action, deliveryId, result },
|
|
464
|
+
{ json: options.json }
|
|
465
|
+
);
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function notificationByName(settings, reference) {
|
|
470
|
+
const notification = (settings.notifications ?? []).find(
|
|
471
|
+
(entry) => entry.id === reference || entry.name === reference
|
|
472
|
+
);
|
|
473
|
+
if (!notification) {
|
|
474
|
+
throw new Error(
|
|
475
|
+
`No notification named "${reference}". Run deepline notifications list.`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
return notification;
|
|
479
|
+
}
|
|
480
|
+
function parseSlackTarget(value) {
|
|
481
|
+
const match = /^slack:(.+)$/i.exec(value.trim());
|
|
482
|
+
if (!match?.[1]) {
|
|
483
|
+
throw new Error(
|
|
484
|
+
"Use --to slack:#channel or --to slack:member:U0123456789."
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
const target = match[1].trim();
|
|
488
|
+
const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(target);
|
|
489
|
+
if (member?.[1]) return { memberId: member[1].toUpperCase() };
|
|
490
|
+
if (target.toLowerCase().startsWith("member:")) {
|
|
491
|
+
throw new Error(
|
|
492
|
+
"Slack member IDs must look like slack:member:U0123456789."
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
return { channel: target };
|
|
496
|
+
}
|
|
497
|
+
function displaySlackTarget(target) {
|
|
498
|
+
return `${target.kind === "member" ? "@" : "#"}${target.name}`;
|
|
499
|
+
}
|
|
500
|
+
function collectEvent(value, previous = []) {
|
|
501
|
+
return [...previous, value];
|
|
502
|
+
}
|
|
503
|
+
function collectNotificationTarget(value, previous = []) {
|
|
504
|
+
return [...previous, value];
|
|
505
|
+
}
|
|
506
|
+
async function readNotificationConfigurationChange(source, read = (path, encoding) => readFileSync(path, encoding)) {
|
|
507
|
+
const label = source === "-" ? "stdin" : source;
|
|
508
|
+
let text;
|
|
509
|
+
try {
|
|
510
|
+
text = await read(source === "-" ? 0 : source, "utf8");
|
|
511
|
+
} catch (error) {
|
|
512
|
+
throw new Error(
|
|
513
|
+
`Could not read notification configuration from ${label}: ${error instanceof Error ? error.message : String(error)}`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
let parsed;
|
|
517
|
+
try {
|
|
518
|
+
parsed = JSON.parse(text);
|
|
519
|
+
} catch (error) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Notification configuration from ${label} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
if (typeof parsed.expectedWorkspaceVersion !== "number" || !Number.isInteger(parsed.expectedWorkspaceVersion) || !parsed.change || typeof parsed.change !== "object") {
|
|
525
|
+
throw new Error(
|
|
526
|
+
`Notification configuration from ${label} must contain an integer expectedWorkspaceVersion and a change object.`
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
expectedWorkspaceVersion: parsed.expectedWorkspaceVersion,
|
|
531
|
+
change: parsed.change
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function registerNotificationCommands(program) {
|
|
535
|
+
const notifications = program.command("notifications").description("Configure notification groups, destinations, and rules.").addHelpText(
|
|
536
|
+
"after",
|
|
537
|
+
`
|
|
538
|
+
Examples:
|
|
539
|
+
deepline notifications add oncall --to email:oncall@example.com --to slack:#oncall
|
|
540
|
+
deepline notifications events
|
|
541
|
+
deepline notifications slack channels --search pipeline
|
|
542
|
+
deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
|
|
543
|
+
deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
|
|
544
|
+
deepline notifications test pipeline-watchdog
|
|
545
|
+
|
|
546
|
+
Each named notification is a group. Repeat --to to give it email and Slack
|
|
547
|
+
destinations. Add rules from the dashboard or notification configuration. The
|
|
548
|
+
existing one-Slack-target form with --for remains supported and creates the
|
|
549
|
+
equivalent compatibility group and rules. Slack connections are managed in
|
|
550
|
+
Dashboard \u2192 Integrations.
|
|
551
|
+
`
|
|
552
|
+
);
|
|
553
|
+
const configuration = notifications.command("configuration").description("Read or atomically update notification groups and rules.").addHelpText(
|
|
554
|
+
"after",
|
|
555
|
+
`
|
|
556
|
+
Examples:
|
|
557
|
+
deepline notifications configuration get --json
|
|
558
|
+
deepline notifications configuration apply change.json --dry-run --json
|
|
559
|
+
jq '{expectedWorkspaceVersion: .configuration.workspaceVersion, change: {kind: "delete_group", groupKey: "oncall"}}' current.json | deepline notifications configuration apply - --json
|
|
560
|
+
`
|
|
561
|
+
);
|
|
562
|
+
configuration.command("get").description("Show the effective group-based notification configuration.").option("--json", "Emit JSON output").action(async (options) => {
|
|
563
|
+
const result = await new DeeplineClient().getNotificationConfiguration();
|
|
564
|
+
printCommandEnvelope(
|
|
565
|
+
{
|
|
566
|
+
...result,
|
|
567
|
+
render: {
|
|
568
|
+
sections: [
|
|
569
|
+
{
|
|
570
|
+
title: "notification groups",
|
|
571
|
+
lines: result.configuration.groups.length ? result.configuration.groups.map(
|
|
572
|
+
(group) => `${group.name}: ${group.enabled ? "active" : "paused"}`
|
|
573
|
+
) : ["No notification groups are configured."]
|
|
574
|
+
}
|
|
575
|
+
]
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{ json: options.json }
|
|
579
|
+
);
|
|
580
|
+
});
|
|
581
|
+
configuration.command("apply").argument(
|
|
582
|
+
"<file>",
|
|
583
|
+
"JSON file, or - for stdin, containing expectedWorkspaceVersion and one desired change"
|
|
584
|
+
).description(
|
|
585
|
+
"Atomically apply a configuration draft exported by an agent or UI."
|
|
586
|
+
).option("--dry-run", "Validate and display the file without saving it").option("--json", "Emit JSON output").action(async (file, options) => {
|
|
587
|
+
const input = await readNotificationConfigurationChange(file);
|
|
588
|
+
if (options.dryRun) {
|
|
589
|
+
printCommandEnvelope(
|
|
590
|
+
{ dryRun: true, ...input },
|
|
591
|
+
{ json: options.json }
|
|
592
|
+
);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const result = await new DeeplineClient().changeNotificationConfiguration(
|
|
596
|
+
input.expectedWorkspaceVersion,
|
|
597
|
+
input.change
|
|
598
|
+
);
|
|
599
|
+
printCommandEnvelope(result, { json: options.json });
|
|
600
|
+
});
|
|
601
|
+
notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
|
|
602
|
+
const settings = await new DeeplineClient().getNotifications();
|
|
603
|
+
printCommandEnvelope(
|
|
604
|
+
{
|
|
605
|
+
events: settings.catalog,
|
|
606
|
+
render: {
|
|
607
|
+
sections: [
|
|
608
|
+
{
|
|
609
|
+
title: "available events",
|
|
610
|
+
lines: settings.catalog.length ? settings.catalog.map(
|
|
611
|
+
(event) => `${event.id}: ${event.description}`
|
|
612
|
+
) : ["No notification events are available."]
|
|
613
|
+
}
|
|
614
|
+
]
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
{ json: options.json }
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
notifications.command("list").description("List named notifications and their selected Play events.").option("--json", "Emit JSON output").option("--compact", "Emit only names, state, target, and event IDs").action(async (options) => {
|
|
621
|
+
const settings = await new DeeplineClient().getNotifications();
|
|
622
|
+
const rules = settings.notifications ?? [];
|
|
623
|
+
printCommandEnvelope(
|
|
624
|
+
{
|
|
625
|
+
notifications: options.compact ? rules.map((rule) => ({
|
|
626
|
+
id: rule.id,
|
|
627
|
+
name: rule.name,
|
|
628
|
+
enabled: rule.enabled,
|
|
629
|
+
target: displaySlackTarget(rule.target),
|
|
630
|
+
eventTypes: rule.eventTypes
|
|
631
|
+
})) : rules,
|
|
632
|
+
count: rules.length,
|
|
633
|
+
render: {
|
|
634
|
+
sections: [
|
|
635
|
+
{
|
|
636
|
+
title: "notifications",
|
|
637
|
+
lines: rules.length ? rules.map(
|
|
638
|
+
(rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 ${displaySlackTarget(rule.target)} (${rule.eventTypes.join(", ") || "no events"})`
|
|
639
|
+
) : [
|
|
640
|
+
"None yet. Add one with deepline notifications add <name> --to slack:#channel --for play.cron.failed."
|
|
641
|
+
]
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
{ json: options.json }
|
|
647
|
+
);
|
|
648
|
+
});
|
|
649
|
+
notifications.command("get").argument("<name>", "Notification name or ID").description("Show one named notification.").option("--json", "Emit JSON output").action(async (name, options) => {
|
|
650
|
+
const rule = notificationByName(
|
|
651
|
+
await new DeeplineClient().getNotifications(),
|
|
652
|
+
name
|
|
653
|
+
);
|
|
654
|
+
printCommandEnvelope({ notification: rule }, { json: options.json });
|
|
655
|
+
});
|
|
656
|
+
notifications.command("add").argument("<name>", "Group name, for example oncall").requiredOption(
|
|
657
|
+
"--to <target>",
|
|
658
|
+
"Target; repeat email:name@example.com, slack:#channel, or slack:member:U0123456789",
|
|
659
|
+
collectNotificationTarget,
|
|
660
|
+
[]
|
|
661
|
+
).option(
|
|
662
|
+
"--for <event>",
|
|
663
|
+
"Legacy one-Slack-target event ID; repeat for more events",
|
|
664
|
+
collectEvent,
|
|
665
|
+
[]
|
|
666
|
+
).option("--dry-run", "Describe the notification without saving it").option("--json", "Emit JSON output").action(
|
|
667
|
+
async (name, options) => {
|
|
668
|
+
const result = await new DeeplineClient().createNotificationGroup({
|
|
669
|
+
name,
|
|
670
|
+
targets: options.to,
|
|
671
|
+
legacyEvents: options.for,
|
|
672
|
+
dryRun: options.dryRun
|
|
673
|
+
});
|
|
674
|
+
if (result.mode === "dry_run") {
|
|
675
|
+
printCommandEnvelope(
|
|
676
|
+
result.compatibilityMode ? {
|
|
677
|
+
dryRun: true,
|
|
678
|
+
name: result.name,
|
|
679
|
+
target: options.to[0],
|
|
680
|
+
eventTypes: result.legacyEvents
|
|
681
|
+
} : {
|
|
682
|
+
dryRun: true,
|
|
683
|
+
group: result.name,
|
|
684
|
+
targets: result.targets,
|
|
685
|
+
compatibilityMode: false
|
|
686
|
+
},
|
|
687
|
+
{ json: options.json }
|
|
688
|
+
);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (result.mode === "legacy") {
|
|
692
|
+
printCommandEnvelope(
|
|
693
|
+
{
|
|
694
|
+
notification: result.notification,
|
|
695
|
+
render: {
|
|
696
|
+
sections: [
|
|
697
|
+
{
|
|
698
|
+
title: "notification saved",
|
|
699
|
+
lines: [name]
|
|
700
|
+
}
|
|
701
|
+
],
|
|
702
|
+
actions: [
|
|
703
|
+
{
|
|
704
|
+
label: "Send a test",
|
|
705
|
+
command: `deepline notifications test ${name}`
|
|
706
|
+
}
|
|
707
|
+
]
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
{ json: options.json }
|
|
711
|
+
);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
printCommandEnvelope(
|
|
715
|
+
{
|
|
716
|
+
...result,
|
|
717
|
+
render: {
|
|
718
|
+
sections: [
|
|
719
|
+
{
|
|
720
|
+
title: "notification group saved",
|
|
721
|
+
lines: [
|
|
722
|
+
result.group.name,
|
|
723
|
+
`${result.destinations.length} destination${result.destinations.length === 1 ? "" : "s"}`,
|
|
724
|
+
"0 rules"
|
|
725
|
+
]
|
|
726
|
+
}
|
|
727
|
+
],
|
|
728
|
+
actions: [
|
|
729
|
+
{
|
|
730
|
+
label: "View configuration",
|
|
731
|
+
command: "deepline notifications configuration get"
|
|
732
|
+
}
|
|
733
|
+
]
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
{ json: options.json }
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
notifications.command("edit").argument("<name>", "Notification name or ID").option(
|
|
741
|
+
"--to <target>",
|
|
742
|
+
"New target, for example slack:#pipeline-alerts or slack:member:U0123456789"
|
|
743
|
+
).option(
|
|
744
|
+
"--for <event>",
|
|
745
|
+
"Replace selected events; repeat for more events. Run notifications events to list them",
|
|
746
|
+
collectEvent,
|
|
747
|
+
[]
|
|
748
|
+
).option("--dry-run", "Describe the change without saving it").option("--json", "Emit JSON output").action(
|
|
749
|
+
async (name, options) => {
|
|
750
|
+
const client = new DeeplineClient();
|
|
751
|
+
const rule = notificationByName(await client.getNotifications(), name);
|
|
752
|
+
const target = options.to ? parseSlackTarget(options.to) : rule.target.kind === "member" ? { memberId: rule.target.id } : { channel: rule.target.name };
|
|
753
|
+
const eventTypes = options.for.length ? options.for : rule.eventTypes;
|
|
754
|
+
if (options.dryRun) {
|
|
755
|
+
printCommandEnvelope(
|
|
756
|
+
{ dryRun: true, notification: rule.id, target, eventTypes },
|
|
757
|
+
{ json: options.json }
|
|
758
|
+
);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const result = await client.updateNotification(rule.id, {
|
|
762
|
+
name: rule.name,
|
|
763
|
+
...target,
|
|
764
|
+
eventTypes
|
|
765
|
+
});
|
|
766
|
+
printCommandEnvelope({ notification: result }, { json: options.json });
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
const slack = notifications.command("slack").description("Select a channel from the connected Slack integration.");
|
|
770
|
+
slack.command("channels").option("--search <text>", "Filter channel names").option("--json", "Emit JSON output").action(async (options) => {
|
|
771
|
+
const result = await new DeeplineClient().listNotificationChannels(
|
|
772
|
+
options.search
|
|
773
|
+
);
|
|
774
|
+
printCommandEnvelope(
|
|
775
|
+
{
|
|
776
|
+
...result,
|
|
777
|
+
render: {
|
|
778
|
+
sections: [
|
|
779
|
+
{
|
|
780
|
+
title: "Slack channels",
|
|
781
|
+
lines: result.channels.length ? result.channels.map((channel) => `#${channel.name}`) : ["No visible channels matched."]
|
|
782
|
+
}
|
|
783
|
+
]
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
{ json: options.json }
|
|
787
|
+
);
|
|
788
|
+
});
|
|
789
|
+
for (const [command, enabled] of [
|
|
790
|
+
["pause", false],
|
|
791
|
+
["resume", true]
|
|
792
|
+
]) {
|
|
793
|
+
notifications.command(command).argument("<name>", "Notification name or ID").option("--dry-run", "Describe the change without saving it").option("--json", "Emit JSON output").action(async (name, options) => {
|
|
794
|
+
const client = new DeeplineClient();
|
|
795
|
+
const rule = notificationByName(await client.getNotifications(), name);
|
|
796
|
+
if (options.dryRun) {
|
|
797
|
+
printCommandEnvelope(
|
|
798
|
+
{ dryRun: true, notification: rule.id, enabled },
|
|
799
|
+
{ json: options.json }
|
|
800
|
+
);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const result = await client.updateNotification(rule.id, { enabled });
|
|
804
|
+
printCommandEnvelope({ notification: result }, { json: options.json });
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
notifications.command("test").argument("<name>", "Notification name or ID").option("--dry-run", "Describe the external send without sending").option("--json", "Emit JSON output").action(async (name, options) => {
|
|
808
|
+
const client = new DeeplineClient();
|
|
809
|
+
const rule = notificationByName(await client.getNotifications(), name);
|
|
810
|
+
if (options.dryRun) {
|
|
811
|
+
printCommandEnvelope(
|
|
812
|
+
{ dryRun: true, notification: rule.id, action: "send_test" },
|
|
813
|
+
{ json: options.json }
|
|
814
|
+
);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
const result = await client.testNotification(rule.id);
|
|
818
|
+
printCommandEnvelope(
|
|
819
|
+
{ ...result, notification: rule.name },
|
|
820
|
+
{ json: options.json }
|
|
821
|
+
);
|
|
822
|
+
});
|
|
823
|
+
notifications.command("delete").argument("<name>", "Notification name or ID").description(
|
|
824
|
+
"Archive one notification without touching its provider integration."
|
|
825
|
+
).option("--dry-run", "Describe the deletion without changing state").option("--json", "Emit JSON output").action(async (name, options) => {
|
|
826
|
+
const client = new DeeplineClient();
|
|
827
|
+
const rule = notificationByName(await client.getNotifications(), name);
|
|
828
|
+
if (options.dryRun) {
|
|
829
|
+
printCommandEnvelope(
|
|
830
|
+
{ dryRun: true, notification: rule.id, action: "archive" },
|
|
831
|
+
{ json: options.json }
|
|
832
|
+
);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const result = await client.deleteNotification(rule.id);
|
|
836
|
+
printCommandEnvelope(
|
|
837
|
+
{ ...result, notification: rule.name },
|
|
838
|
+
{ json: options.json }
|
|
839
|
+
);
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
export {
|
|
843
|
+
readNotificationConfigurationChange,
|
|
844
|
+
registerNotificationCommands,
|
|
845
|
+
registerSettingsCommands
|
|
846
|
+
};
|