@triflux/remote 10.0.1 → 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.
Files changed (155) hide show
  1. package/hub/pipe.mjs +239 -121
  2. package/hub/public/dashboard.html +68 -7
  3. package/hub/server.mjs +2181 -701
  4. package/hub/store-adapter.mjs +101 -37
  5. package/hub/store.mjs +356 -153
  6. package/hub/team/agent-map.json +26 -7
  7. package/hub/team/ansi.mjs +245 -146
  8. package/hub/team/backend.mjs +84 -17
  9. package/hub/team/build-worker-prompt.mjs +42 -0
  10. package/hub/team/check-mcp-hub.mjs +49 -0
  11. package/hub/team/claude-daemon-control.mjs +219 -0
  12. package/hub/team/claude-native-bridge.mjs +784 -0
  13. package/hub/team/claude-session-projection.mjs +65 -0
  14. package/hub/team/cli/commands/attach.mjs +57 -37
  15. package/hub/team/cli/commands/control.mjs +29 -8
  16. package/hub/team/cli/commands/debug.mjs +95 -74
  17. package/hub/team/cli/commands/focus.mjs +80 -53
  18. package/hub/team/cli/commands/interrupt.mjs +18 -6
  19. package/hub/team/cli/commands/kill.mjs +16 -5
  20. package/hub/team/cli/commands/list.mjs +31 -24
  21. package/hub/team/cli/commands/send.mjs +19 -6
  22. package/hub/team/cli/commands/start/index.mjs +154 -33
  23. package/hub/team/cli/commands/start/parse-args.mjs +89 -12
  24. package/hub/team/cli/commands/start/start-headless.mjs +127 -37
  25. package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
  26. package/hub/team/cli/commands/start/start-mux.mjs +122 -73
  27. package/hub/team/cli/commands/start/start-wt.mjs +86 -69
  28. package/hub/team/cli/commands/status.mjs +43 -14
  29. package/hub/team/cli/commands/stop.mjs +11 -4
  30. package/hub/team/cli/commands/task.mjs +8 -3
  31. package/hub/team/cli/commands/tasks.mjs +13 -13
  32. package/hub/team/cli/index.mjs +2 -2
  33. package/hub/team/cli/manifest.mjs +38 -8
  34. package/hub/team/cli/render.mjs +52 -30
  35. package/hub/team/cli/services/attach-fallback.mjs +74 -54
  36. package/hub/team/cli/services/hub-client.mjs +45 -17
  37. package/hub/team/cli/services/member-selector.mjs +37 -30
  38. package/hub/team/cli/services/native-control.mjs +144 -116
  39. package/hub/team/cli/services/runtime-mode.mjs +55 -1
  40. package/hub/team/cli/services/state-store.mjs +66 -9
  41. package/hub/team/cli/services/task-model.mjs +40 -30
  42. package/hub/team/codex-review.mjs +521 -0
  43. package/hub/team/conductor-mesh-bridge.mjs +25 -24
  44. package/hub/team/conductor-registry.mjs +121 -0
  45. package/hub/team/conductor.mjs +1379 -671
  46. package/hub/team/consensus-meta.mjs +176 -0
  47. package/hub/team/dashboard-anchor.mjs +4 -5
  48. package/hub/team/dashboard-layout.mjs +3 -1
  49. package/hub/team/dashboard-open.mjs +21 -119
  50. package/hub/team/dashboard.mjs +76 -28
  51. package/hub/team/event-log.mjs +18 -10
  52. package/hub/team/execution-mode.mjs +241 -0
  53. package/hub/team/extract-completion-payload.mjs +110 -0
  54. package/hub/team/git-preflight.mjs +286 -0
  55. package/hub/team/handoff.mjs +49 -18
  56. package/hub/team/headless-bridge-session.mjs +17 -0
  57. package/hub/team/headless.mjs +2065 -1156
  58. package/hub/team/health-probe.mjs +232 -69
  59. package/hub/team/launcher-template.mjs +23 -13
  60. package/hub/team/lead-control.mjs +91 -4
  61. package/hub/team/mcp-selector.mjs +145 -0
  62. package/hub/team/native-supervisor.mjs +90 -26
  63. package/hub/team/native.mjs +74 -35
  64. package/hub/team/nativeProxy.mjs +184 -113
  65. package/hub/team/notify.mjs +147 -78
  66. package/hub/team/orchestrator.mjs +173 -161
  67. package/hub/team/pane.mjs +43 -15
  68. package/hub/team/process-cleanup.mjs +69 -25
  69. package/hub/team/psmux.mjs +1067 -264
  70. package/hub/team/recovery-store.mjs +149 -0
  71. package/hub/team/remote-probe.mjs +69 -53
  72. package/hub/team/remote-session.mjs +131 -59
  73. package/hub/team/remote-watcher.mjs +62 -34
  74. package/hub/team/retry-state-machine.mjs +267 -0
  75. package/hub/team/routing.mjs +51 -25
  76. package/hub/team/runtime-strategy.mjs +200 -0
  77. package/hub/team/schemas/worker-completion.json +39 -0
  78. package/hub/team/sentinel-capture.mjs +127 -0
  79. package/hub/team/session-sync.mjs +153 -6
  80. package/hub/team/session.mjs +605 -611
  81. package/hub/team/shared.mjs +13 -13
  82. package/hub/team/staleState.mjs +72 -30
  83. package/hub/team/swarm-cli.mjs +390 -0
  84. package/hub/team/swarm-hypervisor.mjs +1526 -198
  85. package/hub/team/swarm-intent.mjs +216 -0
  86. package/hub/team/swarm-locks.mjs +140 -25
  87. package/hub/team/swarm-planner.mjs +121 -21
  88. package/hub/team/swarm-preflight.mjs +317 -0
  89. package/hub/team/swarm-reconciler.mjs +128 -23
  90. package/hub/team/synapse-cli.mjs +458 -0
  91. package/hub/team/synapse-http.mjs +60 -0
  92. package/hub/team/synapse-registry.mjs +329 -0
  93. package/hub/team/terminal-opener.mjs +216 -0
  94. package/hub/team/tui-core.mjs +288 -0
  95. package/hub/team/tui-lite.mjs +234 -146
  96. package/hub/team/tui-remote-adapter.mjs +21 -37
  97. package/hub/team/tui-synapse.mjs +223 -0
  98. package/hub/team/tui-viewer.mjs +104 -45
  99. package/hub/team/tui-widgets.mjs +262 -0
  100. package/hub/team/tui.mjs +936 -449
  101. package/hub/team/worker-completion-validator.mjs +84 -0
  102. package/hub/team/worker-sandbox.mjs +93 -0
  103. package/hub/team/worker-signal.mjs +263 -0
  104. package/hub/team/worker-signal.types.d.ts +52 -0
  105. package/hub/team/worktree-lifecycle.mjs +518 -64
  106. package/hub/team/wt-manager.mjs +297 -26
  107. package/hub/tools.mjs +474 -252
  108. package/hub/tray.mjs +411 -368
  109. package/hub/workers/claude-worker.mjs +162 -118
  110. package/hub/workers/codex-app-server-worker.mjs +1015 -0
  111. package/hub/workers/codex-mcp.mjs +724 -507
  112. package/hub/workers/delegator-mcp.mjs +663 -337
  113. package/hub/workers/factory.mjs +100 -21
  114. package/hub/workers/gemini-worker.mjs +179 -86
  115. package/hub/workers/interface.mjs +23 -2
  116. package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
  117. package/hub/workers/worker-utils.mjs +22 -16
  118. package/package.json +4 -3
  119. package/scripts/lib/agent-json.mjs +27 -0
  120. package/scripts/lib/async.mjs +174 -0
  121. package/scripts/lib/claudemd-scanner.mjs +223 -0
  122. package/scripts/lib/cli-agy.mjs +62 -0
  123. package/scripts/lib/cli-claude.mjs +78 -0
  124. package/scripts/lib/cli-codex.mjs +199 -0
  125. package/scripts/lib/cli-gemini.mjs +67 -0
  126. package/scripts/lib/codex-recovery.sh +50 -0
  127. package/scripts/lib/context.mjs +67 -0
  128. package/scripts/lib/cross-review-utils.mjs +59 -0
  129. package/scripts/lib/doctor-env-checks.mjs +121 -0
  130. package/scripts/lib/dynamic-route-cli.mjs +107 -0
  131. package/scripts/lib/env-probe.mjs +269 -0
  132. package/scripts/lib/env.mjs +14 -0
  133. package/scripts/lib/gemini-profiles.mjs +119 -0
  134. package/scripts/lib/handoff.mjs +187 -0
  135. package/scripts/lib/hook-utils.mjs +16 -0
  136. package/scripts/lib/hub.mjs +48 -0
  137. package/scripts/lib/keyword-rules.mjs +195 -0
  138. package/scripts/lib/logger.mjs +105 -0
  139. package/scripts/lib/mcp-filter.mjs +883 -0
  140. package/scripts/lib/mcp-gateway-servers.mjs +70 -0
  141. package/scripts/lib/mcp-guard-engine.mjs +2120 -0
  142. package/scripts/lib/mcp-health.mjs +773 -0
  143. package/scripts/lib/mcp-manifest.mjs +89 -0
  144. package/scripts/lib/mcp-server-catalog.mjs +355 -0
  145. package/scripts/lib/pid.mjs +63 -0
  146. package/scripts/lib/process-utils.mjs +26 -0
  147. package/scripts/lib/psmux-info.mjs +179 -0
  148. package/scripts/lib/quota.mjs +47 -0
  149. package/scripts/lib/remote-spawn-transfer.mjs +226 -0
  150. package/scripts/lib/skill-state.mjs +220 -0
  151. package/scripts/lib/skill-template.mjs +375 -0
  152. package/scripts/lib/team.mjs +75 -0
  153. package/scripts/lib/timeout.mjs +66 -0
  154. package/scripts/lib/tmp.mjs +26 -0
  155. package/scripts/lib/toml.mjs +113 -0
@@ -1,52 +1,66 @@
1
1
  // hub/team/notify.mjs — team notifier (bell / Windows toast / webhook)
2
2
 
3
- import { execFile } from 'node:child_process';
4
- import os from 'node:os';
3
+ import { execFile } from "node:child_process";
4
+ import os from "node:os";
5
5
 
6
- export const NOTIFY_EVENT_TYPES = Object.freeze(['completed', 'failed', 'inputWait']);
7
- export const NOTIFY_CHANNELS = Object.freeze(['bell', 'toast', 'webhook']);
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 ?? '').replaceAll("'", "''");
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 === '') return new Date().toISOString();
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 !== 'object') {
25
- throw new TypeError('notify(event) requires an event object');
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 || '').trim();
39
+ const type = String(event.type || "").trim();
29
40
  if (!NOTIFY_EVENT_TYPES.includes(type)) {
30
- throw new TypeError(`Unsupported notify event type: ${type || '<empty>'}`);
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 ? '' : String(event.sessionId),
36
- host: event.host == null || event.host === '' ? String(defaults.host || os.hostname()) : String(event.host),
37
- summary: event.summary == null ? '' : String(event.summary),
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 'bell':
58
+ case "bell":
45
59
  return { enabled: true };
46
- case 'toast':
60
+ case "toast":
47
61
  return { enabled: true };
48
- case 'webhook': {
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 = typeof value === 'boolean'
60
- ? { enabled: value }
61
- : (value && typeof value === 'object' ? value : {});
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 ('enabled' in next) {
85
+ if ("enabled" in next) {
69
86
  next.enabled = Boolean(next.enabled);
70
87
  }
71
88
 
72
- if (name === 'webhook') {
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 === 'toast') {
93
+ if (name === "toast") {
77
94
  if (next.command != null) next.command = String(next.command);
78
- if (next.timeoutMs != null) next.timeoutMs = Math.max(1, Number.parseInt(String(next.timeoutMs), 10) || 5000);
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 === 'object' ? 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(channel, { ...channels[channel], ...(typeof config === 'boolean' ? { enabled: config } : (config || {})) }, env),
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 'completed':
107
- return 'Triflux completed';
108
- case 'failed':
109
- return 'Triflux failed';
110
- case 'inputWait':
111
- return 'Triflux waiting for input';
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 'Triflux notification';
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 = [event.sessionId ? `session ${event.sessionId}` : '', event.host ? `host ${event.host}` : '']
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('\n') || event.timestamp;
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) return createResult('bell', 'skipped', { reason: 'disabled' });
179
+ if (!config.enabled)
180
+ return createResult("bell", "skipped", { reason: "disabled" });
149
181
  const stream = deps.stdout;
150
- if (!stream || typeof stream.write !== 'function') {
151
- return createResult('bell', 'skipped', { reason: 'stdout-unavailable' });
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('\u0007');
156
- return createResult('bell', 'sent');
187
+ stream.write("\u0007");
188
+ return createResult("bell", "sent");
157
189
  } catch (error) {
158
- return createResult('bell', 'failed', { error: error.message });
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
- ' New-BurntToastNotification -Text @($Title, $Body) | Out-Null',
171
- ' return',
172
- '}',
173
- '[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null',
174
- '[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null',
175
- '$escapedTitle = [System.Security.SecurityElement]::Escape($Title)',
176
- '$escapedBody = [System.Security.SecurityElement]::Escape($Body)',
177
- '$xml = New-Object Windows.Data.Xml.Dom.XmlDocument',
178
- '$xml.LoadXml("<toast><visual><binding template=\'ToastGeneric\'><text>$escapedTitle</text><text>$escapedBody</text></binding></visual></toast>")',
179
- '$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)',
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) return createResult('toast', 'skipped', { reason: 'disabled' });
186
- if ((deps.platform || process.platform) !== 'win32') {
187
- return createResult('toast', 'skipped', { reason: 'unsupported-platform' });
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
- const execFileFn = deps.execFile || execFile;
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) && deps.powerShellCandidates.length > 0
245
+ : Array.isArray(deps.powerShellCandidates) &&
246
+ deps.powerShellCandidates.length > 0
194
247
  ? deps.powerShellCandidates
195
- : ['pwsh', 'powershell.exe'];
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(command, ['-NoLogo', '-NoProfile', '-Command', script], {
205
- windowsHide: true,
206
- timeout: config.timeoutMs || 5000,
207
- }, execFileFn);
208
- return createResult('toast', 'sent', { command });
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('toast', 'failed', { error: failures.join(' | ') || 'toast-send-failed' });
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) return createResult('webhook', 'skipped', { reason: 'disabled' });
219
- if (!config.url) return createResult('webhook', 'skipped', { reason: 'missing-url' });
220
- if (typeof deps.fetch !== 'function') {
221
- return createResult('webhook', 'failed', { error: 'fetch-unavailable' });
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: 'POST',
227
- headers: { 'content-type': 'application/json' },
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('webhook', 'failed', { error: `HTTP ${response?.status ?? 'unknown'}` });
294
+ return createResult("webhook", "failed", {
295
+ error: `HTTP ${response?.status ?? "unknown"}`,
296
+ });
233
297
  }
234
298
 
235
- return createResult('webhook', 'sent', { statusCode: response.status });
299
+ return createResult("webhook", "sent", { statusCode: response.status });
236
300
  } catch (error) {
237
- return createResult('webhook', 'failed', { error: error.message });
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(updateChannelConfig(channels, channel, config, deps.env), deps);
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(opts.deps?.powerShellCandidates || ['pwsh', 'powershell.exe']),
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
+ }