deepline 0.3.14 → 0.3.15
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/client.ts +25 -13
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +1 -1
- package/dist/bundling-sources/shared_libs/product-notifications/contract.ts +15 -0
- package/dist/cli/index.js +50 -24
- package/dist/cli/index.mjs +50 -24
- package/dist/index.d.mts +25 -15
- package/dist/index.d.ts +25 -15
- package/dist/index.js +9 -5
- package/dist/index.mjs +9 -5
- package/package.json +1 -1
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
|
|
99
99
|
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
100
100
|
} from '../../shared_libs/tool-execution-error.js';
|
|
101
|
+
|
|
101
102
|
import { decodePlayRunPublicStatus } from '../../shared_libs/play-runtime/run-lifecycle-policy.js';
|
|
102
103
|
import {
|
|
103
104
|
legacyRawFromToolResponseRawV2,
|
|
@@ -105,6 +106,20 @@ import {
|
|
|
105
106
|
RAW_V2_TOOL_RESPONSE_CONTRACT,
|
|
106
107
|
} from '../../shared_libs/play-runtime/tool-response-contract.js';
|
|
107
108
|
|
|
109
|
+
export type SlackNotificationTarget =
|
|
110
|
+
| { channel: string; memberId?: never }
|
|
111
|
+
| { channel?: never; memberId: string };
|
|
112
|
+
|
|
113
|
+
export type CreateNotificationInput = {
|
|
114
|
+
name: string;
|
|
115
|
+
provider: 'slack';
|
|
116
|
+
eventTypes: string[];
|
|
117
|
+
} & SlackNotificationTarget;
|
|
118
|
+
|
|
119
|
+
export type UpdateNotificationInput =
|
|
120
|
+
| { enabled: boolean }
|
|
121
|
+
| ({ name: string; eventTypes: string[] } & SlackNotificationTarget);
|
|
122
|
+
|
|
108
123
|
const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
109
124
|
const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
|
|
110
125
|
const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
|
|
@@ -2835,7 +2850,8 @@ export class DeeplineClient {
|
|
|
2835
2850
|
}
|
|
2836
2851
|
|
|
2837
2852
|
type DirectStageResult =
|
|
2838
|
-
{ ref: PlayStagedFileRef }
|
|
2853
|
+
| { ref: PlayStagedFileRef }
|
|
2854
|
+
| { fallbackFile: (typeof files)[number] };
|
|
2839
2855
|
const directResults: DirectStageResult[] = await Promise.all(
|
|
2840
2856
|
files.map(async (file) => {
|
|
2841
2857
|
const upload = uploadByIdentity.get(
|
|
@@ -3930,9 +3946,12 @@ export class DeeplineClient {
|
|
|
3930
3946
|
return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
|
|
3931
3947
|
}
|
|
3932
3948
|
|
|
3933
|
-
/** Select
|
|
3934
|
-
async setNotificationSlack(
|
|
3935
|
-
return this.http.put(
|
|
3949
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
3950
|
+
async setNotificationSlack(destination: string | { memberId: string }) {
|
|
3951
|
+
return this.http.put(
|
|
3952
|
+
'/api/v2/settings/notifications',
|
|
3953
|
+
typeof destination === 'string' ? { channel: destination } : destination,
|
|
3954
|
+
);
|
|
3936
3955
|
}
|
|
3937
3956
|
|
|
3938
3957
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
@@ -3998,21 +4017,14 @@ export class DeeplineClient {
|
|
|
3998
4017
|
}
|
|
3999
4018
|
|
|
4000
4019
|
/** Create a named notification routed through an existing provider integration. */
|
|
4001
|
-
async createNotification(input: {
|
|
4002
|
-
name: string;
|
|
4003
|
-
provider: 'slack';
|
|
4004
|
-
channel: string;
|
|
4005
|
-
eventTypes: string[];
|
|
4006
|
-
}) {
|
|
4020
|
+
async createNotification(input: CreateNotificationInput) {
|
|
4007
4021
|
return this.http.post('/api/v2/notifications', input);
|
|
4008
4022
|
}
|
|
4009
4023
|
|
|
4010
4024
|
/** Update a notification's target, event selection, or enabled state. */
|
|
4011
4025
|
async updateNotification(
|
|
4012
4026
|
notificationId: string,
|
|
4013
|
-
input:
|
|
4014
|
-
| { enabled: boolean }
|
|
4015
|
-
| { name: string; channel: string; eventTypes: string[] },
|
|
4027
|
+
input: UpdateNotificationInput,
|
|
4016
4028
|
) {
|
|
4017
4029
|
return this.http.patch(
|
|
4018
4030
|
`/api/v2/notifications/${encodeURIComponent(notificationId)}`,
|
|
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
|
|
|
192
192
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
193
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
194
|
// getters keep their established compatibility behavior.
|
|
195
|
-
version: '0.3.
|
|
195
|
+
version: '0.3.15',
|
|
196
196
|
updateSummary:
|
|
197
197
|
'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
|
|
198
198
|
contracts: {
|
|
@@ -1136,7 +1136,7 @@ export interface ProductNotification {
|
|
|
1136
1136
|
id: string;
|
|
1137
1137
|
name: string;
|
|
1138
1138
|
provider: string;
|
|
1139
|
-
target: { id: string; name: string };
|
|
1139
|
+
target: { id: string; name: string; kind?: 'channel' | 'member' };
|
|
1140
1140
|
enabled: boolean;
|
|
1141
1141
|
status: string;
|
|
1142
1142
|
eventTypes: string[];
|
|
@@ -79,10 +79,25 @@ export const PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
|
|
|
79
79
|
'channels:read',
|
|
80
80
|
'chat:write',
|
|
81
81
|
'groups:read',
|
|
82
|
+
'im:write',
|
|
82
83
|
] as const;
|
|
83
84
|
export type ProductNotificationDestinationKind =
|
|
84
85
|
(typeof PRODUCT_NOTIFICATION_DESTINATION_KINDS)[number];
|
|
85
86
|
|
|
87
|
+
export const PRODUCT_NOTIFICATION_SLACK_TARGET_KINDS = [
|
|
88
|
+
'channel',
|
|
89
|
+
'member',
|
|
90
|
+
] as const;
|
|
91
|
+
export type ProductNotificationSlackTargetKind =
|
|
92
|
+
(typeof PRODUCT_NOTIFICATION_SLACK_TARGET_KINDS)[number];
|
|
93
|
+
|
|
94
|
+
/** Slack member IDs begin with U (or W for Enterprise Grid workspaces). */
|
|
95
|
+
export function isProductNotificationSlackMemberId(
|
|
96
|
+
value: unknown,
|
|
97
|
+
): value is string {
|
|
98
|
+
return typeof value === 'string' && /^[UW][A-Z0-9]{8,}$/.test(value.trim());
|
|
99
|
+
}
|
|
100
|
+
|
|
86
101
|
export const PRODUCT_NOTIFICATION_DELIVERY_STATES = [
|
|
87
102
|
'pending',
|
|
88
103
|
'delivering',
|
package/dist/cli/index.js
CHANGED
|
@@ -1047,7 +1047,7 @@ var SDK_RELEASE = {
|
|
|
1047
1047
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1048
1048
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1049
1049
|
// getters keep their established compatibility behavior.
|
|
1050
|
-
version: "0.3.
|
|
1050
|
+
version: "0.3.15",
|
|
1051
1051
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1052
1052
|
contracts: {
|
|
1053
1053
|
api: {
|
|
@@ -1907,7 +1907,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
|
|
|
1907
1907
|
var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
|
|
1908
1908
|
"channels:read",
|
|
1909
1909
|
"chat:write",
|
|
1910
|
-
"groups:read"
|
|
1910
|
+
"groups:read",
|
|
1911
|
+
"im:write"
|
|
1911
1912
|
];
|
|
1912
1913
|
var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
|
|
1913
1914
|
0,
|
|
@@ -5987,9 +5988,12 @@ var DeeplineClient = class {
|
|
|
5987
5988
|
const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
|
|
5988
5989
|
return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
|
|
5989
5990
|
}
|
|
5990
|
-
/** Select
|
|
5991
|
-
async setNotificationSlack(
|
|
5992
|
-
return this.http.put(
|
|
5991
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
5992
|
+
async setNotificationSlack(destination) {
|
|
5993
|
+
return this.http.put(
|
|
5994
|
+
"/api/v2/settings/notifications",
|
|
5995
|
+
typeof destination === "string" ? { channel: destination } : destination
|
|
5996
|
+
);
|
|
5993
5997
|
}
|
|
5994
5998
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
5995
5999
|
async testNotificationSlack() {
|
|
@@ -36120,7 +36124,7 @@ async function printSettings(options) {
|
|
|
36120
36124
|
title: "Slack destination",
|
|
36121
36125
|
lines: slack ? [
|
|
36122
36126
|
`status: ${String(slack.status ?? "unknown")}`,
|
|
36123
|
-
|
|
36127
|
+
`${slack.targetKind === "member" ? "member" : "channel"}: ${slack.targetKind === "member" ? "@" : "#"}${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
|
|
36124
36128
|
...slack.lastErrorMessage ? [`issue: ${String(slack.lastErrorMessage)}`] : []
|
|
36125
36129
|
] : ["Not configured."]
|
|
36126
36130
|
},
|
|
@@ -36224,27 +36228,32 @@ notifications; there is no general integrations CLI.
|
|
|
36224
36228
|
{ json: options.json }
|
|
36225
36229
|
);
|
|
36226
36230
|
});
|
|
36227
|
-
notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").
|
|
36231
|
+
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(
|
|
36228
36232
|
async (destination, options) => {
|
|
36229
36233
|
if (destination !== "slack")
|
|
36230
36234
|
throw new Error("Only slack is supported.");
|
|
36235
|
+
if (Boolean(options.channel) === Boolean(options.memberId)) {
|
|
36236
|
+
throw new Error("Provide exactly one of --channel or --member-id.");
|
|
36237
|
+
}
|
|
36238
|
+
const target = options.memberId ? { memberId: options.memberId } : options.channel;
|
|
36231
36239
|
if (options.dryRun) {
|
|
36232
36240
|
printCommandEnvelope(
|
|
36233
|
-
{ dryRun: true, destination,
|
|
36241
|
+
{ dryRun: true, destination, target },
|
|
36234
36242
|
{ json: options.json }
|
|
36235
36243
|
);
|
|
36236
36244
|
return;
|
|
36237
36245
|
}
|
|
36238
|
-
const result = await new DeeplineClient().setNotificationSlack(
|
|
36239
|
-
options.channel
|
|
36240
|
-
);
|
|
36246
|
+
const result = await new DeeplineClient().setNotificationSlack(target);
|
|
36241
36247
|
printCommandEnvelope(
|
|
36242
36248
|
{
|
|
36243
36249
|
ok: true,
|
|
36244
36250
|
result,
|
|
36245
36251
|
render: {
|
|
36246
36252
|
sections: [
|
|
36247
|
-
{
|
|
36253
|
+
{
|
|
36254
|
+
title: "Slack destination saved",
|
|
36255
|
+
lines: [options.memberId ?? options.channel]
|
|
36256
|
+
}
|
|
36248
36257
|
],
|
|
36249
36258
|
actions: [
|
|
36250
36259
|
{
|
|
@@ -36446,9 +36455,22 @@ function notificationByName(settings, reference) {
|
|
|
36446
36455
|
function parseSlackTarget(value) {
|
|
36447
36456
|
const match = /^slack:(.+)$/i.exec(value.trim());
|
|
36448
36457
|
if (!match?.[1]) {
|
|
36449
|
-
throw new Error(
|
|
36458
|
+
throw new Error(
|
|
36459
|
+
"Use --to slack:#channel or --to slack:member:U0123456789."
|
|
36460
|
+
);
|
|
36450
36461
|
}
|
|
36451
|
-
|
|
36462
|
+
const target = match[1].trim();
|
|
36463
|
+
const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(target);
|
|
36464
|
+
if (member?.[1]) return { memberId: member[1].toUpperCase() };
|
|
36465
|
+
if (target.toLowerCase().startsWith("member:")) {
|
|
36466
|
+
throw new Error(
|
|
36467
|
+
"Slack member IDs must look like slack:member:U0123456789."
|
|
36468
|
+
);
|
|
36469
|
+
}
|
|
36470
|
+
return { channel: target };
|
|
36471
|
+
}
|
|
36472
|
+
function displaySlackTarget(target) {
|
|
36473
|
+
return `${target.kind === "member" ? "@" : "#"}${target.name}`;
|
|
36452
36474
|
}
|
|
36453
36475
|
function collectEvent(value, previous = []) {
|
|
36454
36476
|
return [...previous, value];
|
|
@@ -36461,10 +36483,11 @@ Examples:
|
|
|
36461
36483
|
deepline notifications events
|
|
36462
36484
|
deepline notifications slack channels --search pipeline
|
|
36463
36485
|
deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
|
|
36486
|
+
deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
|
|
36464
36487
|
deepline notifications test pipeline-watchdog
|
|
36465
36488
|
|
|
36466
36489
|
Slack connections are managed in Dashboard \u2192 Integrations. This command only
|
|
36467
|
-
chooses the connected Slack channel and the events it receives.
|
|
36490
|
+
chooses the connected Slack channel or member and the events it receives.
|
|
36468
36491
|
`
|
|
36469
36492
|
);
|
|
36470
36493
|
notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
|
|
@@ -36495,7 +36518,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36495
36518
|
id: rule.id,
|
|
36496
36519
|
name: rule.name,
|
|
36497
36520
|
enabled: rule.enabled,
|
|
36498
|
-
target:
|
|
36521
|
+
target: displaySlackTarget(rule.target),
|
|
36499
36522
|
eventTypes: rule.eventTypes
|
|
36500
36523
|
})) : rules,
|
|
36501
36524
|
count: rules.length,
|
|
@@ -36504,7 +36527,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36504
36527
|
{
|
|
36505
36528
|
title: "notifications",
|
|
36506
36529
|
lines: rules.length ? rules.map(
|
|
36507
|
-
(rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192
|
|
36530
|
+
(rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 ${displaySlackTarget(rule.target)} (${rule.eventTypes.join(", ") || "no events"})`
|
|
36508
36531
|
) : [
|
|
36509
36532
|
"None yet. Add one with deepline notifications add <name> --to slack:#channel --for play.cron.failed."
|
|
36510
36533
|
]
|
|
@@ -36524,7 +36547,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36524
36547
|
});
|
|
36525
36548
|
notifications.command("add").argument("<name>", "Short stable name, for example pipeline-watchdog").requiredOption(
|
|
36526
36549
|
"--to <target>",
|
|
36527
|
-
"Target, for example slack:#pipeline-alerts"
|
|
36550
|
+
"Target, for example slack:#pipeline-alerts or slack:member:U0123456789"
|
|
36528
36551
|
).option(
|
|
36529
36552
|
"--for <event>",
|
|
36530
36553
|
"Event ID; repeat for more events. Run notifications events to list them",
|
|
@@ -36535,7 +36558,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36535
36558
|
if (!options.for.length) {
|
|
36536
36559
|
throw new Error("Choose at least one event with --for <event>.");
|
|
36537
36560
|
}
|
|
36538
|
-
const
|
|
36561
|
+
const target = parseSlackTarget(options.to);
|
|
36539
36562
|
if (options.dryRun) {
|
|
36540
36563
|
printCommandEnvelope(
|
|
36541
36564
|
{ dryRun: true, name, target: options.to, eventTypes: options.for },
|
|
@@ -36546,7 +36569,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36546
36569
|
const result = await new DeeplineClient().createNotification({
|
|
36547
36570
|
name,
|
|
36548
36571
|
provider: "slack",
|
|
36549
|
-
|
|
36572
|
+
...target,
|
|
36550
36573
|
eventTypes: options.for
|
|
36551
36574
|
});
|
|
36552
36575
|
printCommandEnvelope(
|
|
@@ -36566,7 +36589,10 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36566
36589
|
);
|
|
36567
36590
|
}
|
|
36568
36591
|
);
|
|
36569
|
-
notifications.command("edit").argument("<name>", "Notification name or ID").option(
|
|
36592
|
+
notifications.command("edit").argument("<name>", "Notification name or ID").option(
|
|
36593
|
+
"--to <target>",
|
|
36594
|
+
"New target, for example slack:#pipeline-alerts or slack:member:U0123456789"
|
|
36595
|
+
).option(
|
|
36570
36596
|
"--for <event>",
|
|
36571
36597
|
"Replace selected events; repeat for more events. Run notifications events to list them",
|
|
36572
36598
|
collectEvent,
|
|
@@ -36575,18 +36601,18 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36575
36601
|
async (name, options) => {
|
|
36576
36602
|
const client2 = new DeeplineClient();
|
|
36577
36603
|
const rule = notificationByName(await client2.getNotifications(), name);
|
|
36578
|
-
const
|
|
36604
|
+
const target = options.to ? parseSlackTarget(options.to) : rule.target.kind === "member" ? { memberId: rule.target.id } : { channel: rule.target.name };
|
|
36579
36605
|
const eventTypes = options.for.length ? options.for : rule.eventTypes;
|
|
36580
36606
|
if (options.dryRun) {
|
|
36581
36607
|
printCommandEnvelope(
|
|
36582
|
-
{ dryRun: true, notification: rule.id,
|
|
36608
|
+
{ dryRun: true, notification: rule.id, target, eventTypes },
|
|
36583
36609
|
{ json: options.json }
|
|
36584
36610
|
);
|
|
36585
36611
|
return;
|
|
36586
36612
|
}
|
|
36587
36613
|
const result = await client2.updateNotification(rule.id, {
|
|
36588
36614
|
name: rule.name,
|
|
36589
|
-
|
|
36615
|
+
...target,
|
|
36590
36616
|
eventTypes
|
|
36591
36617
|
});
|
|
36592
36618
|
printCommandEnvelope({ notification: result }, { json: options.json });
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1033,7 +1033,7 @@ var SDK_RELEASE = {
|
|
|
1033
1033
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
1034
1034
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
1035
1035
|
// getters keep their established compatibility behavior.
|
|
1036
|
-
version: "0.3.
|
|
1036
|
+
version: "0.3.15",
|
|
1037
1037
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
1038
1038
|
contracts: {
|
|
1039
1039
|
api: {
|
|
@@ -1893,7 +1893,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
|
|
|
1893
1893
|
var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
|
|
1894
1894
|
"channels:read",
|
|
1895
1895
|
"chat:write",
|
|
1896
|
-
"groups:read"
|
|
1896
|
+
"groups:read",
|
|
1897
|
+
"im:write"
|
|
1897
1898
|
];
|
|
1898
1899
|
var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
|
|
1899
1900
|
0,
|
|
@@ -5973,9 +5974,12 @@ var DeeplineClient = class {
|
|
|
5973
5974
|
const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
|
|
5974
5975
|
return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
|
|
5975
5976
|
}
|
|
5976
|
-
/** Select
|
|
5977
|
-
async setNotificationSlack(
|
|
5978
|
-
return this.http.put(
|
|
5977
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
5978
|
+
async setNotificationSlack(destination) {
|
|
5979
|
+
return this.http.put(
|
|
5980
|
+
"/api/v2/settings/notifications",
|
|
5981
|
+
typeof destination === "string" ? { channel: destination } : destination
|
|
5982
|
+
);
|
|
5979
5983
|
}
|
|
5980
5984
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
5981
5985
|
async testNotificationSlack() {
|
|
@@ -36198,7 +36202,7 @@ async function printSettings(options) {
|
|
|
36198
36202
|
title: "Slack destination",
|
|
36199
36203
|
lines: slack ? [
|
|
36200
36204
|
`status: ${String(slack.status ?? "unknown")}`,
|
|
36201
|
-
|
|
36205
|
+
`${slack.targetKind === "member" ? "member" : "channel"}: ${slack.targetKind === "member" ? "@" : "#"}${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
|
|
36202
36206
|
...slack.lastErrorMessage ? [`issue: ${String(slack.lastErrorMessage)}`] : []
|
|
36203
36207
|
] : ["Not configured."]
|
|
36204
36208
|
},
|
|
@@ -36302,27 +36306,32 @@ notifications; there is no general integrations CLI.
|
|
|
36302
36306
|
{ json: options.json }
|
|
36303
36307
|
);
|
|
36304
36308
|
});
|
|
36305
|
-
notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").
|
|
36309
|
+
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(
|
|
36306
36310
|
async (destination, options) => {
|
|
36307
36311
|
if (destination !== "slack")
|
|
36308
36312
|
throw new Error("Only slack is supported.");
|
|
36313
|
+
if (Boolean(options.channel) === Boolean(options.memberId)) {
|
|
36314
|
+
throw new Error("Provide exactly one of --channel or --member-id.");
|
|
36315
|
+
}
|
|
36316
|
+
const target = options.memberId ? { memberId: options.memberId } : options.channel;
|
|
36309
36317
|
if (options.dryRun) {
|
|
36310
36318
|
printCommandEnvelope(
|
|
36311
|
-
{ dryRun: true, destination,
|
|
36319
|
+
{ dryRun: true, destination, target },
|
|
36312
36320
|
{ json: options.json }
|
|
36313
36321
|
);
|
|
36314
36322
|
return;
|
|
36315
36323
|
}
|
|
36316
|
-
const result = await new DeeplineClient().setNotificationSlack(
|
|
36317
|
-
options.channel
|
|
36318
|
-
);
|
|
36324
|
+
const result = await new DeeplineClient().setNotificationSlack(target);
|
|
36319
36325
|
printCommandEnvelope(
|
|
36320
36326
|
{
|
|
36321
36327
|
ok: true,
|
|
36322
36328
|
result,
|
|
36323
36329
|
render: {
|
|
36324
36330
|
sections: [
|
|
36325
|
-
{
|
|
36331
|
+
{
|
|
36332
|
+
title: "Slack destination saved",
|
|
36333
|
+
lines: [options.memberId ?? options.channel]
|
|
36334
|
+
}
|
|
36326
36335
|
],
|
|
36327
36336
|
actions: [
|
|
36328
36337
|
{
|
|
@@ -36524,9 +36533,22 @@ function notificationByName(settings, reference) {
|
|
|
36524
36533
|
function parseSlackTarget(value) {
|
|
36525
36534
|
const match = /^slack:(.+)$/i.exec(value.trim());
|
|
36526
36535
|
if (!match?.[1]) {
|
|
36527
|
-
throw new Error(
|
|
36536
|
+
throw new Error(
|
|
36537
|
+
"Use --to slack:#channel or --to slack:member:U0123456789."
|
|
36538
|
+
);
|
|
36528
36539
|
}
|
|
36529
|
-
|
|
36540
|
+
const target = match[1].trim();
|
|
36541
|
+
const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(target);
|
|
36542
|
+
if (member?.[1]) return { memberId: member[1].toUpperCase() };
|
|
36543
|
+
if (target.toLowerCase().startsWith("member:")) {
|
|
36544
|
+
throw new Error(
|
|
36545
|
+
"Slack member IDs must look like slack:member:U0123456789."
|
|
36546
|
+
);
|
|
36547
|
+
}
|
|
36548
|
+
return { channel: target };
|
|
36549
|
+
}
|
|
36550
|
+
function displaySlackTarget(target) {
|
|
36551
|
+
return `${target.kind === "member" ? "@" : "#"}${target.name}`;
|
|
36530
36552
|
}
|
|
36531
36553
|
function collectEvent(value, previous = []) {
|
|
36532
36554
|
return [...previous, value];
|
|
@@ -36539,10 +36561,11 @@ Examples:
|
|
|
36539
36561
|
deepline notifications events
|
|
36540
36562
|
deepline notifications slack channels --search pipeline
|
|
36541
36563
|
deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
|
|
36564
|
+
deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
|
|
36542
36565
|
deepline notifications test pipeline-watchdog
|
|
36543
36566
|
|
|
36544
36567
|
Slack connections are managed in Dashboard \u2192 Integrations. This command only
|
|
36545
|
-
chooses the connected Slack channel and the events it receives.
|
|
36568
|
+
chooses the connected Slack channel or member and the events it receives.
|
|
36546
36569
|
`
|
|
36547
36570
|
);
|
|
36548
36571
|
notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
|
|
@@ -36573,7 +36596,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36573
36596
|
id: rule.id,
|
|
36574
36597
|
name: rule.name,
|
|
36575
36598
|
enabled: rule.enabled,
|
|
36576
|
-
target:
|
|
36599
|
+
target: displaySlackTarget(rule.target),
|
|
36577
36600
|
eventTypes: rule.eventTypes
|
|
36578
36601
|
})) : rules,
|
|
36579
36602
|
count: rules.length,
|
|
@@ -36582,7 +36605,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36582
36605
|
{
|
|
36583
36606
|
title: "notifications",
|
|
36584
36607
|
lines: rules.length ? rules.map(
|
|
36585
|
-
(rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192
|
|
36608
|
+
(rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 ${displaySlackTarget(rule.target)} (${rule.eventTypes.join(", ") || "no events"})`
|
|
36586
36609
|
) : [
|
|
36587
36610
|
"None yet. Add one with deepline notifications add <name> --to slack:#channel --for play.cron.failed."
|
|
36588
36611
|
]
|
|
@@ -36602,7 +36625,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36602
36625
|
});
|
|
36603
36626
|
notifications.command("add").argument("<name>", "Short stable name, for example pipeline-watchdog").requiredOption(
|
|
36604
36627
|
"--to <target>",
|
|
36605
|
-
"Target, for example slack:#pipeline-alerts"
|
|
36628
|
+
"Target, for example slack:#pipeline-alerts or slack:member:U0123456789"
|
|
36606
36629
|
).option(
|
|
36607
36630
|
"--for <event>",
|
|
36608
36631
|
"Event ID; repeat for more events. Run notifications events to list them",
|
|
@@ -36613,7 +36636,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36613
36636
|
if (!options.for.length) {
|
|
36614
36637
|
throw new Error("Choose at least one event with --for <event>.");
|
|
36615
36638
|
}
|
|
36616
|
-
const
|
|
36639
|
+
const target = parseSlackTarget(options.to);
|
|
36617
36640
|
if (options.dryRun) {
|
|
36618
36641
|
printCommandEnvelope(
|
|
36619
36642
|
{ dryRun: true, name, target: options.to, eventTypes: options.for },
|
|
@@ -36624,7 +36647,7 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36624
36647
|
const result = await new DeeplineClient().createNotification({
|
|
36625
36648
|
name,
|
|
36626
36649
|
provider: "slack",
|
|
36627
|
-
|
|
36650
|
+
...target,
|
|
36628
36651
|
eventTypes: options.for
|
|
36629
36652
|
});
|
|
36630
36653
|
printCommandEnvelope(
|
|
@@ -36644,7 +36667,10 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36644
36667
|
);
|
|
36645
36668
|
}
|
|
36646
36669
|
);
|
|
36647
|
-
notifications.command("edit").argument("<name>", "Notification name or ID").option(
|
|
36670
|
+
notifications.command("edit").argument("<name>", "Notification name or ID").option(
|
|
36671
|
+
"--to <target>",
|
|
36672
|
+
"New target, for example slack:#pipeline-alerts or slack:member:U0123456789"
|
|
36673
|
+
).option(
|
|
36648
36674
|
"--for <event>",
|
|
36649
36675
|
"Replace selected events; repeat for more events. Run notifications events to list them",
|
|
36650
36676
|
collectEvent,
|
|
@@ -36653,18 +36679,18 @@ chooses the connected Slack channel and the events it receives.
|
|
|
36653
36679
|
async (name, options) => {
|
|
36654
36680
|
const client2 = new DeeplineClient();
|
|
36655
36681
|
const rule = notificationByName(await client2.getNotifications(), name);
|
|
36656
|
-
const
|
|
36682
|
+
const target = options.to ? parseSlackTarget(options.to) : rule.target.kind === "member" ? { memberId: rule.target.id } : { channel: rule.target.name };
|
|
36657
36683
|
const eventTypes = options.for.length ? options.for : rule.eventTypes;
|
|
36658
36684
|
if (options.dryRun) {
|
|
36659
36685
|
printCommandEnvelope(
|
|
36660
|
-
{ dryRun: true, notification: rule.id,
|
|
36686
|
+
{ dryRun: true, notification: rule.id, target, eventTypes },
|
|
36661
36687
|
{ json: options.json }
|
|
36662
36688
|
);
|
|
36663
36689
|
return;
|
|
36664
36690
|
}
|
|
36665
36691
|
const result = await client2.updateNotification(rule.id, {
|
|
36666
36692
|
name: rule.name,
|
|
36667
|
-
|
|
36693
|
+
...target,
|
|
36668
36694
|
eventTypes
|
|
36669
36695
|
});
|
|
36670
36696
|
printCommandEnvelope({ notification: result }, { json: options.json });
|
package/dist/index.d.mts
CHANGED
|
@@ -1243,6 +1243,7 @@ interface ProductNotification {
|
|
|
1243
1243
|
target: {
|
|
1244
1244
|
id: string;
|
|
1245
1245
|
name: string;
|
|
1246
|
+
kind?: 'channel' | 'member';
|
|
1246
1247
|
};
|
|
1247
1248
|
enabled: boolean;
|
|
1248
1249
|
status: string;
|
|
@@ -1894,6 +1895,24 @@ type EnrichCompiledConfig = {
|
|
|
1894
1895
|
};
|
|
1895
1896
|
};
|
|
1896
1897
|
|
|
1898
|
+
type SlackNotificationTarget = {
|
|
1899
|
+
channel: string;
|
|
1900
|
+
memberId?: never;
|
|
1901
|
+
} | {
|
|
1902
|
+
channel?: never;
|
|
1903
|
+
memberId: string;
|
|
1904
|
+
};
|
|
1905
|
+
type CreateNotificationInput = {
|
|
1906
|
+
name: string;
|
|
1907
|
+
provider: 'slack';
|
|
1908
|
+
eventTypes: string[];
|
|
1909
|
+
} & SlackNotificationTarget;
|
|
1910
|
+
type UpdateNotificationInput = {
|
|
1911
|
+
enabled: boolean;
|
|
1912
|
+
} | ({
|
|
1913
|
+
name: string;
|
|
1914
|
+
eventTypes: string[];
|
|
1915
|
+
} & SlackNotificationTarget);
|
|
1897
1916
|
type IngestionStorageRepairResult = {
|
|
1898
1917
|
status?: 'repaired';
|
|
1899
1918
|
connection_grants: {
|
|
@@ -3364,8 +3383,10 @@ declare class DeeplineClient {
|
|
|
3364
3383
|
isPrivate: boolean;
|
|
3365
3384
|
}>;
|
|
3366
3385
|
}>;
|
|
3367
|
-
/** Select
|
|
3368
|
-
setNotificationSlack(
|
|
3386
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
3387
|
+
setNotificationSlack(destination: string | {
|
|
3388
|
+
memberId: string;
|
|
3389
|
+
}): Promise<unknown>;
|
|
3369
3390
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
3370
3391
|
testNotificationSlack(): Promise<{
|
|
3371
3392
|
ok: boolean;
|
|
@@ -3398,20 +3419,9 @@ declare class DeeplineClient {
|
|
|
3398
3419
|
}>;
|
|
3399
3420
|
}>;
|
|
3400
3421
|
/** Create a named notification routed through an existing provider integration. */
|
|
3401
|
-
createNotification(input:
|
|
3402
|
-
name: string;
|
|
3403
|
-
provider: 'slack';
|
|
3404
|
-
channel: string;
|
|
3405
|
-
eventTypes: string[];
|
|
3406
|
-
}): Promise<unknown>;
|
|
3422
|
+
createNotification(input: CreateNotificationInput): Promise<unknown>;
|
|
3407
3423
|
/** Update a notification's target, event selection, or enabled state. */
|
|
3408
|
-
updateNotification(notificationId: string, input:
|
|
3409
|
-
enabled: boolean;
|
|
3410
|
-
} | {
|
|
3411
|
-
name: string;
|
|
3412
|
-
channel: string;
|
|
3413
|
-
eventTypes: string[];
|
|
3414
|
-
}): Promise<unknown>;
|
|
3424
|
+
updateNotification(notificationId: string, input: UpdateNotificationInput): Promise<unknown>;
|
|
3415
3425
|
/** Send a validation ping to one notification. */
|
|
3416
3426
|
testNotification(notificationId: string): Promise<{
|
|
3417
3427
|
ok: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1243,6 +1243,7 @@ interface ProductNotification {
|
|
|
1243
1243
|
target: {
|
|
1244
1244
|
id: string;
|
|
1245
1245
|
name: string;
|
|
1246
|
+
kind?: 'channel' | 'member';
|
|
1246
1247
|
};
|
|
1247
1248
|
enabled: boolean;
|
|
1248
1249
|
status: string;
|
|
@@ -1894,6 +1895,24 @@ type EnrichCompiledConfig = {
|
|
|
1894
1895
|
};
|
|
1895
1896
|
};
|
|
1896
1897
|
|
|
1898
|
+
type SlackNotificationTarget = {
|
|
1899
|
+
channel: string;
|
|
1900
|
+
memberId?: never;
|
|
1901
|
+
} | {
|
|
1902
|
+
channel?: never;
|
|
1903
|
+
memberId: string;
|
|
1904
|
+
};
|
|
1905
|
+
type CreateNotificationInput = {
|
|
1906
|
+
name: string;
|
|
1907
|
+
provider: 'slack';
|
|
1908
|
+
eventTypes: string[];
|
|
1909
|
+
} & SlackNotificationTarget;
|
|
1910
|
+
type UpdateNotificationInput = {
|
|
1911
|
+
enabled: boolean;
|
|
1912
|
+
} | ({
|
|
1913
|
+
name: string;
|
|
1914
|
+
eventTypes: string[];
|
|
1915
|
+
} & SlackNotificationTarget);
|
|
1897
1916
|
type IngestionStorageRepairResult = {
|
|
1898
1917
|
status?: 'repaired';
|
|
1899
1918
|
connection_grants: {
|
|
@@ -3364,8 +3383,10 @@ declare class DeeplineClient {
|
|
|
3364
3383
|
isPrivate: boolean;
|
|
3365
3384
|
}>;
|
|
3366
3385
|
}>;
|
|
3367
|
-
/** Select
|
|
3368
|
-
setNotificationSlack(
|
|
3386
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
3387
|
+
setNotificationSlack(destination: string | {
|
|
3388
|
+
memberId: string;
|
|
3389
|
+
}): Promise<unknown>;
|
|
3369
3390
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
3370
3391
|
testNotificationSlack(): Promise<{
|
|
3371
3392
|
ok: boolean;
|
|
@@ -3398,20 +3419,9 @@ declare class DeeplineClient {
|
|
|
3398
3419
|
}>;
|
|
3399
3420
|
}>;
|
|
3400
3421
|
/** Create a named notification routed through an existing provider integration. */
|
|
3401
|
-
createNotification(input:
|
|
3402
|
-
name: string;
|
|
3403
|
-
provider: 'slack';
|
|
3404
|
-
channel: string;
|
|
3405
|
-
eventTypes: string[];
|
|
3406
|
-
}): Promise<unknown>;
|
|
3422
|
+
createNotification(input: CreateNotificationInput): Promise<unknown>;
|
|
3407
3423
|
/** Update a notification's target, event selection, or enabled state. */
|
|
3408
|
-
updateNotification(notificationId: string, input:
|
|
3409
|
-
enabled: boolean;
|
|
3410
|
-
} | {
|
|
3411
|
-
name: string;
|
|
3412
|
-
channel: string;
|
|
3413
|
-
eventTypes: string[];
|
|
3414
|
-
}): Promise<unknown>;
|
|
3424
|
+
updateNotification(notificationId: string, input: UpdateNotificationInput): Promise<unknown>;
|
|
3415
3425
|
/** Send a validation ping to one notification. */
|
|
3416
3426
|
testNotification(notificationId: string): Promise<{
|
|
3417
3427
|
ok: boolean;
|
package/dist/index.js
CHANGED
|
@@ -783,7 +783,7 @@ var SDK_RELEASE = {
|
|
|
783
783
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
784
784
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
785
785
|
// getters keep their established compatibility behavior.
|
|
786
|
-
version: "0.3.
|
|
786
|
+
version: "0.3.15",
|
|
787
787
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
788
788
|
contracts: {
|
|
789
789
|
api: {
|
|
@@ -1643,7 +1643,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
|
|
|
1643
1643
|
var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
|
|
1644
1644
|
"channels:read",
|
|
1645
1645
|
"chat:write",
|
|
1646
|
-
"groups:read"
|
|
1646
|
+
"groups:read",
|
|
1647
|
+
"im:write"
|
|
1647
1648
|
];
|
|
1648
1649
|
var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
|
|
1649
1650
|
0,
|
|
@@ -5723,9 +5724,12 @@ var DeeplineClient = class {
|
|
|
5723
5724
|
const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
|
|
5724
5725
|
return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
|
|
5725
5726
|
}
|
|
5726
|
-
/** Select
|
|
5727
|
-
async setNotificationSlack(
|
|
5728
|
-
return this.http.put(
|
|
5727
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
5728
|
+
async setNotificationSlack(destination) {
|
|
5729
|
+
return this.http.put(
|
|
5730
|
+
"/api/v2/settings/notifications",
|
|
5731
|
+
typeof destination === "string" ? { channel: destination } : destination
|
|
5732
|
+
);
|
|
5729
5733
|
}
|
|
5730
5734
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
5731
5735
|
async testNotificationSlack() {
|
package/dist/index.mjs
CHANGED
|
@@ -706,7 +706,7 @@ var SDK_RELEASE = {
|
|
|
706
706
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
707
707
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
708
708
|
// getters keep their established compatibility behavior.
|
|
709
|
-
version: "0.3.
|
|
709
|
+
version: "0.3.15",
|
|
710
710
|
updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
|
|
711
711
|
contracts: {
|
|
712
712
|
api: {
|
|
@@ -1566,7 +1566,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
|
|
|
1566
1566
|
var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
|
|
1567
1567
|
"channels:read",
|
|
1568
1568
|
"chat:write",
|
|
1569
|
-
"groups:read"
|
|
1569
|
+
"groups:read",
|
|
1570
|
+
"im:write"
|
|
1570
1571
|
];
|
|
1571
1572
|
var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
|
|
1572
1573
|
0,
|
|
@@ -5646,9 +5647,12 @@ var DeeplineClient = class {
|
|
|
5646
5647
|
const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
|
|
5647
5648
|
return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
|
|
5648
5649
|
}
|
|
5649
|
-
/** Select
|
|
5650
|
-
async setNotificationSlack(
|
|
5651
|
-
return this.http.put(
|
|
5650
|
+
/** Select a Slack channel or direct member used for product notifications. */
|
|
5651
|
+
async setNotificationSlack(destination) {
|
|
5652
|
+
return this.http.put(
|
|
5653
|
+
"/api/v2/settings/notifications",
|
|
5654
|
+
typeof destination === "string" ? { channel: destination } : destination
|
|
5655
|
+
);
|
|
5652
5656
|
}
|
|
5653
5657
|
/** Send one synchronous test ping and return Slack's delivery result. */
|
|
5654
5658
|
async testNotificationSlack() {
|