@triflux/remote 10.1.0 → 10.25.1
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/hub/pipe.mjs +239 -121
- package/hub/public/dashboard.html +61 -0
- package/hub/server.mjs +2165 -708
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +26 -7
- package/hub/team/ansi.mjs +245 -146
- package/hub/team/backend.mjs +84 -17
- package/hub/team/build-worker-prompt.mjs +42 -0
- package/hub/team/check-mcp-hub.mjs +49 -0
- package/hub/team/claude-daemon-control.mjs +219 -0
- package/hub/team/claude-native-bridge.mjs +784 -0
- package/hub/team/claude-session-projection.mjs +65 -0
- package/hub/team/cli/commands/attach.mjs +57 -37
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +95 -74
- package/hub/team/cli/commands/focus.mjs +80 -53
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +31 -24
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -33
- package/hub/team/cli/commands/start/parse-args.mjs +89 -12
- package/hub/team/cli/commands/start/start-headless.mjs +125 -37
- package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
- package/hub/team/cli/commands/start/start-mux.mjs +122 -73
- package/hub/team/cli/commands/start/start-wt.mjs +86 -69
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +52 -30
- package/hub/team/cli/services/attach-fallback.mjs +74 -54
- package/hub/team/cli/services/hub-client.mjs +44 -16
- package/hub/team/cli/services/member-selector.mjs +37 -30
- package/hub/team/cli/services/native-control.mjs +144 -116
- package/hub/team/cli/services/runtime-mode.mjs +55 -1
- package/hub/team/cli/services/state-store.mjs +66 -9
- package/hub/team/cli/services/task-model.mjs +40 -30
- package/hub/team/codex-review.mjs +521 -0
- package/hub/team/conductor-mesh-bridge.mjs +25 -24
- package/hub/team/conductor-registry.mjs +121 -0
- package/hub/team/conductor.mjs +1379 -671
- package/hub/team/consensus-meta.mjs +176 -0
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +20 -118
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/execution-mode.mjs +241 -0
- package/hub/team/extract-completion-payload.mjs +110 -0
- package/hub/team/git-preflight.mjs +286 -0
- package/hub/team/handoff.mjs +49 -18
- package/hub/team/headless-bridge-session.mjs +17 -0
- package/hub/team/headless.mjs +2065 -1156
- package/hub/team/health-probe.mjs +232 -69
- package/hub/team/launcher-template.mjs +23 -13
- package/hub/team/lead-control.mjs +91 -4
- package/hub/team/mcp-selector.mjs +145 -0
- package/hub/team/native-supervisor.mjs +90 -26
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +147 -78
- package/hub/team/orchestrator.mjs +173 -161
- package/hub/team/pane.mjs +43 -15
- package/hub/team/process-cleanup.mjs +69 -25
- package/hub/team/psmux.mjs +1020 -279
- package/hub/team/recovery-store.mjs +149 -0
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +131 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +200 -0
- package/hub/team/schemas/worker-completion.json +39 -0
- package/hub/team/sentinel-capture.mjs +127 -0
- package/hub/team/session-sync.mjs +153 -6
- package/hub/team/session.mjs +605 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-cli.mjs +390 -0
- package/hub/team/swarm-hypervisor.mjs +1522 -193
- package/hub/team/swarm-intent.mjs +216 -0
- package/hub/team/swarm-locks.mjs +140 -25
- package/hub/team/swarm-planner.mjs +121 -21
- package/hub/team/swarm-preflight.mjs +317 -0
- package/hub/team/swarm-reconciler.mjs +128 -23
- package/hub/team/synapse-cli.mjs +458 -0
- package/hub/team/synapse-http.mjs +60 -0
- package/hub/team/synapse-registry.mjs +329 -0
- package/hub/team/terminal-opener.mjs +216 -0
- package/hub/team/tui-core.mjs +288 -0
- package/hub/team/tui-lite.mjs +234 -146
- package/hub/team/tui-remote-adapter.mjs +18 -34
- package/hub/team/tui-synapse.mjs +223 -0
- package/hub/team/tui-viewer.mjs +104 -44
- package/hub/team/tui-widgets.mjs +262 -0
- package/hub/team/tui.mjs +927 -437
- package/hub/team/worker-completion-validator.mjs +84 -0
- package/hub/team/worker-sandbox.mjs +93 -0
- package/hub/team/worker-signal.mjs +263 -0
- package/hub/team/worker-signal.types.d.ts +52 -0
- package/hub/team/worktree-lifecycle.mjs +517 -63
- package/hub/team/wt-manager.mjs +297 -26
- package/hub/tools.mjs +474 -252
- package/hub/tray.mjs +411 -368
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-app-server-worker.mjs +1015 -0
- package/hub/workers/codex-mcp.mjs +724 -507
- package/hub/workers/delegator-mcp.mjs +662 -335
- package/hub/workers/factory.mjs +100 -21
- package/hub/workers/gemini-worker.mjs +179 -86
- package/hub/workers/interface.mjs +23 -2
- package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
- package/hub/workers/worker-utils.mjs +22 -16
- package/package.json +4 -3
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +223 -0
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +67 -0
- package/scripts/lib/cross-review-utils.mjs +59 -0
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +269 -0
- package/scripts/lib/env.mjs +14 -0
- package/scripts/lib/gemini-profiles.mjs +119 -0
- package/scripts/lib/handoff.mjs +187 -0
- package/scripts/lib/hook-utils.mjs +16 -0
- package/scripts/lib/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +195 -0
- package/scripts/lib/logger.mjs +105 -0
- package/scripts/lib/mcp-filter.mjs +883 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-guard-engine.mjs +2120 -0
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +89 -0
- package/scripts/lib/mcp-server-catalog.mjs +355 -0
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +179 -0
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +226 -0
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +375 -0
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
package/hub/team/notify.mjs
CHANGED
|
@@ -1,52 +1,66 @@
|
|
|
1
1
|
// hub/team/notify.mjs — team notifier (bell / Windows toast / webhook)
|
|
2
2
|
|
|
3
|
-
import { execFile } from
|
|
4
|
-
import os from
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import os from "node:os";
|
|
5
5
|
|
|
6
|
-
export const NOTIFY_EVENT_TYPES = Object.freeze([
|
|
7
|
-
|
|
6
|
+
export const NOTIFY_EVENT_TYPES = Object.freeze([
|
|
7
|
+
"completed",
|
|
8
|
+
"failed",
|
|
9
|
+
"inputWait",
|
|
10
|
+
]);
|
|
11
|
+
export const NOTIFY_CHANNELS = Object.freeze(["bell", "toast", "webhook"]);
|
|
8
12
|
|
|
9
13
|
function freezeRecord(record) {
|
|
10
14
|
return Object.freeze({ ...record });
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
function escapePowerShellSingleQuoted(value) {
|
|
14
|
-
return String(value ??
|
|
18
|
+
return String(value ?? "").replaceAll("'", "''");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function escapeAppleScriptString(value) {
|
|
22
|
+
return String(value ?? "")
|
|
23
|
+
.replaceAll("\\", "\\\\")
|
|
24
|
+
.replaceAll('"', '\\"')
|
|
25
|
+
.replace(/\r?\n/g, " ");
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
function normalizeTimestamp(value) {
|
|
18
29
|
if (value instanceof Date) return value.toISOString();
|
|
19
|
-
if (value == null || value ===
|
|
30
|
+
if (value == null || value === "") return new Date().toISOString();
|
|
20
31
|
return String(value);
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
function normalizeEvent(event, defaults = {}) {
|
|
24
|
-
if (!event || typeof event !==
|
|
25
|
-
throw new TypeError(
|
|
35
|
+
if (!event || typeof event !== "object") {
|
|
36
|
+
throw new TypeError("notify(event) requires an event object");
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
const type = String(event.type ||
|
|
39
|
+
const type = String(event.type || "").trim();
|
|
29
40
|
if (!NOTIFY_EVENT_TYPES.includes(type)) {
|
|
30
|
-
throw new TypeError(`Unsupported notify event type: ${type ||
|
|
41
|
+
throw new TypeError(`Unsupported notify event type: ${type || "<empty>"}`);
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
return freezeRecord({
|
|
34
45
|
type,
|
|
35
|
-
sessionId: event.sessionId == null ?
|
|
36
|
-
host:
|
|
37
|
-
|
|
46
|
+
sessionId: event.sessionId == null ? "" : String(event.sessionId),
|
|
47
|
+
host:
|
|
48
|
+
event.host == null || event.host === ""
|
|
49
|
+
? String(defaults.host || os.hostname())
|
|
50
|
+
: String(event.host),
|
|
51
|
+
summary: event.summary == null ? "" : String(event.summary),
|
|
38
52
|
timestamp: normalizeTimestamp(event.timestamp),
|
|
39
53
|
});
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
function defaultChannelConfig(name, env) {
|
|
43
57
|
switch (name) {
|
|
44
|
-
case
|
|
58
|
+
case "bell":
|
|
45
59
|
return { enabled: true };
|
|
46
|
-
case
|
|
60
|
+
case "toast":
|
|
47
61
|
return { enabled: true };
|
|
48
|
-
case
|
|
49
|
-
const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK ||
|
|
62
|
+
case "webhook": {
|
|
63
|
+
const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK || "");
|
|
50
64
|
return { enabled: Boolean(url), url };
|
|
51
65
|
}
|
|
52
66
|
default:
|
|
@@ -56,33 +70,40 @@ function defaultChannelConfig(name, env) {
|
|
|
56
70
|
|
|
57
71
|
function normalizeChannelConfig(name, value, env) {
|
|
58
72
|
const base = defaultChannelConfig(name, env);
|
|
59
|
-
const patch =
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
const patch =
|
|
74
|
+
typeof value === "boolean"
|
|
75
|
+
? { enabled: value }
|
|
76
|
+
: value && typeof value === "object"
|
|
77
|
+
? value
|
|
78
|
+
: {};
|
|
62
79
|
|
|
63
80
|
const next = {
|
|
64
81
|
...base,
|
|
65
82
|
...patch,
|
|
66
83
|
};
|
|
67
84
|
|
|
68
|
-
if (
|
|
85
|
+
if ("enabled" in next) {
|
|
69
86
|
next.enabled = Boolean(next.enabled);
|
|
70
87
|
}
|
|
71
88
|
|
|
72
|
-
if (name ===
|
|
73
|
-
next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK ||
|
|
89
|
+
if (name === "webhook") {
|
|
90
|
+
next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK || "");
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
if (name ===
|
|
93
|
+
if (name === "toast") {
|
|
77
94
|
if (next.command != null) next.command = String(next.command);
|
|
78
|
-
if (next.timeoutMs != null)
|
|
95
|
+
if (next.timeoutMs != null)
|
|
96
|
+
next.timeoutMs = Math.max(
|
|
97
|
+
1,
|
|
98
|
+
Number.parseInt(String(next.timeoutMs), 10) || 5000,
|
|
99
|
+
);
|
|
79
100
|
}
|
|
80
101
|
|
|
81
102
|
return freezeRecord(next);
|
|
82
103
|
}
|
|
83
104
|
|
|
84
105
|
function normalizeChannels(channels, env) {
|
|
85
|
-
const source = channels && typeof channels ===
|
|
106
|
+
const source = channels && typeof channels === "object" ? channels : {};
|
|
86
107
|
const normalized = {};
|
|
87
108
|
for (const name of NOTIFY_CHANNELS) {
|
|
88
109
|
normalized[name] = normalizeChannelConfig(name, source[name], env);
|
|
@@ -97,20 +118,27 @@ function updateChannelConfig(channels, channel, config, env) {
|
|
|
97
118
|
|
|
98
119
|
return Object.freeze({
|
|
99
120
|
...channels,
|
|
100
|
-
[channel]: normalizeChannelConfig(
|
|
121
|
+
[channel]: normalizeChannelConfig(
|
|
122
|
+
channel,
|
|
123
|
+
{
|
|
124
|
+
...channels[channel],
|
|
125
|
+
...(typeof config === "boolean" ? { enabled: config } : config || {}),
|
|
126
|
+
},
|
|
127
|
+
env,
|
|
128
|
+
),
|
|
101
129
|
});
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
function formatEventTitle(event) {
|
|
105
133
|
switch (event.type) {
|
|
106
|
-
case
|
|
107
|
-
return
|
|
108
|
-
case
|
|
109
|
-
return
|
|
110
|
-
case
|
|
111
|
-
return
|
|
134
|
+
case "completed":
|
|
135
|
+
return "Triflux completed";
|
|
136
|
+
case "failed":
|
|
137
|
+
return "Triflux failed";
|
|
138
|
+
case "inputWait":
|
|
139
|
+
return "Triflux waiting for input";
|
|
112
140
|
default:
|
|
113
|
-
return
|
|
141
|
+
return "Triflux notification";
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
|
|
@@ -118,12 +146,15 @@ function formatEventBody(event) {
|
|
|
118
146
|
const parts = [];
|
|
119
147
|
if (event.summary) parts.push(event.summary);
|
|
120
148
|
|
|
121
|
-
const meta = [
|
|
149
|
+
const meta = [
|
|
150
|
+
event.sessionId ? `session ${event.sessionId}` : "",
|
|
151
|
+
event.host ? `host ${event.host}` : "",
|
|
152
|
+
]
|
|
122
153
|
.filter(Boolean)
|
|
123
|
-
.join(
|
|
154
|
+
.join(" · ");
|
|
124
155
|
if (meta) parts.push(meta);
|
|
125
156
|
|
|
126
|
-
return parts.join(
|
|
157
|
+
return parts.join("\n") || event.timestamp;
|
|
127
158
|
}
|
|
128
159
|
|
|
129
160
|
function createResult(channel, status, extra = {}) {
|
|
@@ -145,17 +176,18 @@ function execFileAsync(command, args, options, execFileFn) {
|
|
|
145
176
|
}
|
|
146
177
|
|
|
147
178
|
async function sendBell(config, deps) {
|
|
148
|
-
if (!config.enabled)
|
|
179
|
+
if (!config.enabled)
|
|
180
|
+
return createResult("bell", "skipped", { reason: "disabled" });
|
|
149
181
|
const stream = deps.stdout;
|
|
150
|
-
if (!stream || typeof stream.write !==
|
|
151
|
-
return createResult(
|
|
182
|
+
if (!stream || typeof stream.write !== "function") {
|
|
183
|
+
return createResult("bell", "skipped", { reason: "stdout-unavailable" });
|
|
152
184
|
}
|
|
153
185
|
|
|
154
186
|
try {
|
|
155
|
-
stream.write(
|
|
156
|
-
return createResult(
|
|
187
|
+
stream.write("\u0007");
|
|
188
|
+
return createResult("bell", "sent");
|
|
157
189
|
} catch (error) {
|
|
158
|
-
return createResult(
|
|
190
|
+
return createResult("bell", "failed", { error: error.message });
|
|
159
191
|
}
|
|
160
192
|
}
|
|
161
193
|
|
|
@@ -167,32 +199,53 @@ function buildToastScript(title, body) {
|
|
|
167
199
|
`$Title = '${safeTitle}'`,
|
|
168
200
|
`$Body = '${safeBody}'`,
|
|
169
201
|
"if (Get-Command New-BurntToastNotification -ErrorAction SilentlyContinue) {",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
202
|
+
" New-BurntToastNotification -Text @($Title, $Body) | Out-Null",
|
|
203
|
+
" return",
|
|
204
|
+
"}",
|
|
205
|
+
"[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null",
|
|
206
|
+
"[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null",
|
|
207
|
+
"$escapedTitle = [System.Security.SecurityElement]::Escape($Title)",
|
|
208
|
+
"$escapedBody = [System.Security.SecurityElement]::Escape($Body)",
|
|
209
|
+
"$xml = New-Object Windows.Data.Xml.Dom.XmlDocument",
|
|
210
|
+
"$xml.LoadXml(\"<toast><visual><binding template='ToastGeneric'><text>$escapedTitle</text><text>$escapedBody</text></binding></visual></toast>\")",
|
|
211
|
+
"$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)",
|
|
180
212
|
"[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Triflux').Show($toast)",
|
|
181
|
-
].join(
|
|
213
|
+
].join("; ");
|
|
182
214
|
}
|
|
183
215
|
|
|
184
216
|
async function sendToast(event, config, deps) {
|
|
185
|
-
if (!config.enabled)
|
|
186
|
-
|
|
187
|
-
|
|
217
|
+
if (!config.enabled)
|
|
218
|
+
return createResult("toast", "skipped", { reason: "disabled" });
|
|
219
|
+
const execFileFn = deps.execFile || execFile;
|
|
220
|
+
|
|
221
|
+
// macOS: osascript 네이티브 알림
|
|
222
|
+
if ((deps.platform || process.platform) === "darwin") {
|
|
223
|
+
const title = formatEventTitle(event);
|
|
224
|
+
const body = formatEventBody(event);
|
|
225
|
+
const safeTitle = escapeAppleScriptString(title);
|
|
226
|
+
const safeBody = escapeAppleScriptString(body);
|
|
227
|
+
try {
|
|
228
|
+
await execFileAsync(
|
|
229
|
+
"osascript",
|
|
230
|
+
["-e", `display notification "${safeBody}" with title "${safeTitle}"`],
|
|
231
|
+
{ timeout: config.timeoutMs || 5000 },
|
|
232
|
+
execFileFn,
|
|
233
|
+
);
|
|
234
|
+
return createResult("toast", "sent", { command: "osascript" });
|
|
235
|
+
} catch (error) {
|
|
236
|
+
return createResult("toast", "failed", { error: error.message });
|
|
237
|
+
}
|
|
188
238
|
}
|
|
189
239
|
|
|
190
|
-
|
|
240
|
+
if ((deps.platform || process.platform) !== "win32") {
|
|
241
|
+
return createResult("toast", "skipped", { reason: "unsupported-platform" });
|
|
242
|
+
}
|
|
191
243
|
const candidates = config.command
|
|
192
244
|
? [config.command]
|
|
193
|
-
: Array.isArray(deps.powerShellCandidates) &&
|
|
245
|
+
: Array.isArray(deps.powerShellCandidates) &&
|
|
246
|
+
deps.powerShellCandidates.length > 0
|
|
194
247
|
? deps.powerShellCandidates
|
|
195
|
-
: [
|
|
248
|
+
: ["pwsh", "powershell.exe"];
|
|
196
249
|
|
|
197
250
|
const title = formatEventTitle(event);
|
|
198
251
|
const body = formatEventBody(event);
|
|
@@ -201,40 +254,51 @@ async function sendToast(event, config, deps) {
|
|
|
201
254
|
|
|
202
255
|
for (const command of candidates) {
|
|
203
256
|
try {
|
|
204
|
-
await execFileAsync(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
257
|
+
await execFileAsync(
|
|
258
|
+
command,
|
|
259
|
+
["-NoLogo", "-NoProfile", "-Command", script],
|
|
260
|
+
{
|
|
261
|
+
windowsHide: true,
|
|
262
|
+
timeout: config.timeoutMs || 5000,
|
|
263
|
+
},
|
|
264
|
+
execFileFn,
|
|
265
|
+
);
|
|
266
|
+
return createResult("toast", "sent", { command });
|
|
209
267
|
} catch (error) {
|
|
210
268
|
failures.push(`${command}: ${error.message}`);
|
|
211
269
|
}
|
|
212
270
|
}
|
|
213
271
|
|
|
214
|
-
return createResult(
|
|
272
|
+
return createResult("toast", "failed", {
|
|
273
|
+
error: failures.join(" | ") || "toast-send-failed",
|
|
274
|
+
});
|
|
215
275
|
}
|
|
216
276
|
|
|
217
277
|
async function sendWebhook(event, config, deps) {
|
|
218
|
-
if (!config.enabled)
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
return createResult(
|
|
278
|
+
if (!config.enabled)
|
|
279
|
+
return createResult("webhook", "skipped", { reason: "disabled" });
|
|
280
|
+
if (!config.url)
|
|
281
|
+
return createResult("webhook", "skipped", { reason: "missing-url" });
|
|
282
|
+
if (typeof deps.fetch !== "function") {
|
|
283
|
+
return createResult("webhook", "failed", { error: "fetch-unavailable" });
|
|
222
284
|
}
|
|
223
285
|
|
|
224
286
|
try {
|
|
225
287
|
const response = await deps.fetch(config.url, {
|
|
226
|
-
method:
|
|
227
|
-
headers: {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "content-type": "application/json" },
|
|
228
290
|
body: JSON.stringify(event),
|
|
229
291
|
});
|
|
230
292
|
|
|
231
293
|
if (!response?.ok) {
|
|
232
|
-
return createResult(
|
|
294
|
+
return createResult("webhook", "failed", {
|
|
295
|
+
error: `HTTP ${response?.status ?? "unknown"}`,
|
|
296
|
+
});
|
|
233
297
|
}
|
|
234
298
|
|
|
235
|
-
return createResult(
|
|
299
|
+
return createResult("webhook", "sent", { statusCode: response.status });
|
|
236
300
|
} catch (error) {
|
|
237
|
-
return createResult(
|
|
301
|
+
return createResult("webhook", "failed", { error: error.message });
|
|
238
302
|
}
|
|
239
303
|
}
|
|
240
304
|
|
|
@@ -254,7 +318,10 @@ function createNotifierInstance(channels, deps) {
|
|
|
254
318
|
}
|
|
255
319
|
|
|
256
320
|
function setChannel(channel, config) {
|
|
257
|
-
return createNotifierInstance(
|
|
321
|
+
return createNotifierInstance(
|
|
322
|
+
updateChannelConfig(channels, channel, config, deps.env),
|
|
323
|
+
deps,
|
|
324
|
+
);
|
|
258
325
|
}
|
|
259
326
|
|
|
260
327
|
return Object.freeze({ notify, setChannel });
|
|
@@ -286,8 +353,10 @@ export function createNotifier(opts = {}) {
|
|
|
286
353
|
fetch: opts.deps?.fetch || globalThis.fetch?.bind(globalThis),
|
|
287
354
|
platform: opts.platform || process.platform,
|
|
288
355
|
hostname: opts.hostname || os.hostname(),
|
|
289
|
-
powerShellCandidates: Object.freeze(
|
|
356
|
+
powerShellCandidates: Object.freeze(
|
|
357
|
+
opts.deps?.powerShellCandidates || ["pwsh", "powershell.exe"],
|
|
358
|
+
),
|
|
290
359
|
});
|
|
291
360
|
|
|
292
361
|
return createNotifierInstance(normalizeChannels(opts.channels, env), deps);
|
|
293
|
-
}
|
|
362
|
+
}
|