@switchbot/openapi-cli 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.js +57419 -170
- package/package.json +9 -5
- package/dist/api/client.d.ts +0 -31
- package/dist/api/client.js +0 -236
- package/dist/api/client.js.map +0 -1
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -21
- package/dist/auth.js.map +0 -1
- package/dist/commands/agent-bootstrap.d.ts +0 -10
- package/dist/commands/agent-bootstrap.js +0 -200
- package/dist/commands/agent-bootstrap.js.map +0 -1
- package/dist/commands/auth.d.ts +0 -18
- package/dist/commands/auth.js +0 -355
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/batch.d.ts +0 -2
- package/dist/commands/batch.js +0 -414
- package/dist/commands/batch.js.map +0 -1
- package/dist/commands/cache.d.ts +0 -2
- package/dist/commands/cache.js +0 -127
- package/dist/commands/cache.js.map +0 -1
- package/dist/commands/capabilities.d.ts +0 -31
- package/dist/commands/capabilities.js +0 -383
- package/dist/commands/capabilities.js.map +0 -1
- package/dist/commands/catalog.d.ts +0 -2
- package/dist/commands/catalog.js +0 -360
- package/dist/commands/catalog.js.map +0 -1
- package/dist/commands/completion.d.ts +0 -2
- package/dist/commands/completion.js +0 -386
- package/dist/commands/completion.js.map +0 -1
- package/dist/commands/config.d.ts +0 -21
- package/dist/commands/config.js +0 -377
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/daemon.d.ts +0 -2
- package/dist/commands/daemon.js +0 -411
- package/dist/commands/daemon.js.map +0 -1
- package/dist/commands/device-meta.d.ts +0 -2
- package/dist/commands/device-meta.js +0 -160
- package/dist/commands/device-meta.js.map +0 -1
- package/dist/commands/devices.d.ts +0 -2
- package/dist/commands/devices.js +0 -949
- package/dist/commands/devices.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -3
- package/dist/commands/doctor.js +0 -1016
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/events.d.ts +0 -31
- package/dist/commands/events.js +0 -564
- package/dist/commands/events.js.map +0 -1
- package/dist/commands/expand.d.ts +0 -2
- package/dist/commands/expand.js +0 -131
- package/dist/commands/expand.js.map +0 -1
- package/dist/commands/explain.d.ts +0 -2
- package/dist/commands/explain.js +0 -140
- package/dist/commands/explain.js.map +0 -1
- package/dist/commands/health.d.ts +0 -8
- package/dist/commands/health.js +0 -114
- package/dist/commands/health.js.map +0 -1
- package/dist/commands/history.d.ts +0 -2
- package/dist/commands/history.js +0 -321
- package/dist/commands/history.js.map +0 -1
- package/dist/commands/identity.d.ts +0 -45
- package/dist/commands/identity.js +0 -60
- package/dist/commands/identity.js.map +0 -1
- package/dist/commands/install.d.ts +0 -20
- package/dist/commands/install.js +0 -247
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -14
- package/dist/commands/mcp.js +0 -2018
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/plan.d.ts +0 -51
- package/dist/commands/plan.js +0 -654
- package/dist/commands/plan.js.map +0 -1
- package/dist/commands/policy.d.ts +0 -24
- package/dist/commands/policy.js +0 -587
- package/dist/commands/policy.js.map +0 -1
- package/dist/commands/quota.d.ts +0 -2
- package/dist/commands/quota.js +0 -79
- package/dist/commands/quota.js.map +0 -1
- package/dist/commands/rules.d.ts +0 -2
- package/dist/commands/rules.js +0 -876
- package/dist/commands/rules.js.map +0 -1
- package/dist/commands/scenes.d.ts +0 -2
- package/dist/commands/scenes.js +0 -265
- package/dist/commands/scenes.js.map +0 -1
- package/dist/commands/schema.d.ts +0 -2
- package/dist/commands/schema.js +0 -185
- package/dist/commands/schema.js.map +0 -1
- package/dist/commands/status-sync.d.ts +0 -2
- package/dist/commands/status-sync.js +0 -132
- package/dist/commands/status-sync.js.map +0 -1
- package/dist/commands/uninstall.d.ts +0 -20
- package/dist/commands/uninstall.js +0 -238
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/upgrade-check.d.ts +0 -2
- package/dist/commands/upgrade-check.js +0 -107
- package/dist/commands/upgrade-check.js.map +0 -1
- package/dist/commands/watch.d.ts +0 -2
- package/dist/commands/watch.js +0 -195
- package/dist/commands/watch.js.map +0 -1
- package/dist/commands/webhook.d.ts +0 -2
- package/dist/commands/webhook.js +0 -183
- package/dist/commands/webhook.js.map +0 -1
- package/dist/config.d.ts +0 -57
- package/dist/config.js +0 -259
- package/dist/config.js.map +0 -1
- package/dist/credentials/backends/file.d.ts +0 -18
- package/dist/credentials/backends/file.js +0 -102
- package/dist/credentials/backends/file.js.map +0 -1
- package/dist/credentials/backends/linux.d.ts +0 -16
- package/dist/credentials/backends/linux.js +0 -130
- package/dist/credentials/backends/linux.js.map +0 -1
- package/dist/credentials/backends/macos.d.ts +0 -18
- package/dist/credentials/backends/macos.js +0 -130
- package/dist/credentials/backends/macos.js.map +0 -1
- package/dist/credentials/backends/windows.d.ts +0 -23
- package/dist/credentials/backends/windows.js +0 -216
- package/dist/credentials/backends/windows.js.map +0 -1
- package/dist/credentials/keychain.d.ts +0 -83
- package/dist/credentials/keychain.js +0 -89
- package/dist/credentials/keychain.js.map +0 -1
- package/dist/credentials/prime.d.ts +0 -32
- package/dist/credentials/prime.js +0 -53
- package/dist/credentials/prime.js.map +0 -1
- package/dist/devices/cache.d.ts +0 -79
- package/dist/devices/cache.js +0 -294
- package/dist/devices/cache.js.map +0 -1
- package/dist/devices/catalog.d.ts +0 -138
- package/dist/devices/catalog.js +0 -768
- package/dist/devices/catalog.js.map +0 -1
- package/dist/devices/device-meta.d.ts +0 -15
- package/dist/devices/device-meta.js +0 -57
- package/dist/devices/device-meta.js.map +0 -1
- package/dist/devices/history-agg.d.ts +0 -37
- package/dist/devices/history-agg.js +0 -139
- package/dist/devices/history-agg.js.map +0 -1
- package/dist/devices/history-query.d.ts +0 -45
- package/dist/devices/history-query.js +0 -182
- package/dist/devices/history-query.js.map +0 -1
- package/dist/devices/param-validator.d.ts +0 -40
- package/dist/devices/param-validator.js +0 -434
- package/dist/devices/param-validator.js.map +0 -1
- package/dist/devices/resources.d.ts +0 -74
- package/dist/devices/resources.js +0 -271
- package/dist/devices/resources.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/install/default-steps.d.ts +0 -66
- package/dist/install/default-steps.js +0 -258
- package/dist/install/default-steps.js.map +0 -1
- package/dist/install/preflight.d.ts +0 -60
- package/dist/install/preflight.js +0 -213
- package/dist/install/preflight.js.map +0 -1
- package/dist/install/steps.d.ts +0 -61
- package/dist/install/steps.js +0 -68
- package/dist/install/steps.js.map +0 -1
- package/dist/lib/command-keywords.d.ts +0 -5
- package/dist/lib/command-keywords.js +0 -18
- package/dist/lib/command-keywords.js.map +0 -1
- package/dist/lib/daemon-state.d.ts +0 -24
- package/dist/lib/daemon-state.js +0 -47
- package/dist/lib/daemon-state.js.map +0 -1
- package/dist/lib/destructive-mode.d.ts +0 -2
- package/dist/lib/destructive-mode.js +0 -13
- package/dist/lib/destructive-mode.js.map +0 -1
- package/dist/lib/devices.d.ts +0 -151
- package/dist/lib/devices.js +0 -383
- package/dist/lib/devices.js.map +0 -1
- package/dist/lib/idempotency.d.ts +0 -46
- package/dist/lib/idempotency.js +0 -107
- package/dist/lib/idempotency.js.map +0 -1
- package/dist/lib/plan-store.d.ts +0 -19
- package/dist/lib/plan-store.js +0 -69
- package/dist/lib/plan-store.js.map +0 -1
- package/dist/lib/request-context.d.ts +0 -7
- package/dist/lib/request-context.js +0 -13
- package/dist/lib/request-context.js.map +0 -1
- package/dist/lib/scenes.d.ts +0 -7
- package/dist/lib/scenes.js +0 -11
- package/dist/lib/scenes.js.map +0 -1
- package/dist/logger.d.ts +0 -4
- package/dist/logger.js +0 -17
- package/dist/logger.js.map +0 -1
- package/dist/mcp/device-history.d.ts +0 -36
- package/dist/mcp/device-history.js +0 -146
- package/dist/mcp/device-history.js.map +0 -1
- package/dist/mcp/events-subscription.d.ts +0 -45
- package/dist/mcp/events-subscription.js +0 -214
- package/dist/mcp/events-subscription.js.map +0 -1
- package/dist/mqtt/client.d.ts +0 -25
- package/dist/mqtt/client.js +0 -181
- package/dist/mqtt/client.js.map +0 -1
- package/dist/mqtt/credential.d.ts +0 -16
- package/dist/mqtt/credential.js +0 -31
- package/dist/mqtt/credential.js.map +0 -1
- package/dist/policy/add-rule.d.ts +0 -21
- package/dist/policy/add-rule.js +0 -125
- package/dist/policy/add-rule.js.map +0 -1
- package/dist/policy/diff.d.ts +0 -21
- package/dist/policy/diff.js +0 -92
- package/dist/policy/diff.js.map +0 -1
- package/dist/policy/format.d.ts +0 -6
- package/dist/policy/format.js +0 -58
- package/dist/policy/format.js.map +0 -1
- package/dist/policy/load.d.ts +0 -32
- package/dist/policy/load.js +0 -62
- package/dist/policy/load.js.map +0 -1
- package/dist/policy/migrate.d.ts +0 -21
- package/dist/policy/migrate.js +0 -68
- package/dist/policy/migrate.js.map +0 -1
- package/dist/policy/schema.d.ts +0 -5
- package/dist/policy/schema.js +0 -19
- package/dist/policy/schema.js.map +0 -1
- package/dist/policy/validate.d.ts +0 -19
- package/dist/policy/validate.js +0 -263
- package/dist/policy/validate.js.map +0 -1
- package/dist/rules/action.d.ts +0 -65
- package/dist/rules/action.js +0 -217
- package/dist/rules/action.js.map +0 -1
- package/dist/rules/audit-query.d.ts +0 -51
- package/dist/rules/audit-query.js +0 -90
- package/dist/rules/audit-query.js.map +0 -1
- package/dist/rules/conflict-analyzer.d.ts +0 -57
- package/dist/rules/conflict-analyzer.js +0 -215
- package/dist/rules/conflict-analyzer.js.map +0 -1
- package/dist/rules/cron-scheduler.d.ts +0 -62
- package/dist/rules/cron-scheduler.js +0 -187
- package/dist/rules/cron-scheduler.js.map +0 -1
- package/dist/rules/destructive.d.ts +0 -20
- package/dist/rules/destructive.js +0 -53
- package/dist/rules/destructive.js.map +0 -1
- package/dist/rules/engine.d.ts +0 -193
- package/dist/rules/engine.js +0 -758
- package/dist/rules/engine.js.map +0 -1
- package/dist/rules/matcher.d.ts +0 -56
- package/dist/rules/matcher.js +0 -231
- package/dist/rules/matcher.js.map +0 -1
- package/dist/rules/pid-file.d.ts +0 -43
- package/dist/rules/pid-file.js +0 -96
- package/dist/rules/pid-file.js.map +0 -1
- package/dist/rules/quiet-hours.d.ts +0 -26
- package/dist/rules/quiet-hours.js +0 -46
- package/dist/rules/quiet-hours.js.map +0 -1
- package/dist/rules/suggest.d.ts +0 -20
- package/dist/rules/suggest.js +0 -96
- package/dist/rules/suggest.js.map +0 -1
- package/dist/rules/throttle.d.ts +0 -61
- package/dist/rules/throttle.js +0 -117
- package/dist/rules/throttle.js.map +0 -1
- package/dist/rules/types.d.ts +0 -117
- package/dist/rules/types.js +0 -35
- package/dist/rules/types.js.map +0 -1
- package/dist/rules/webhook-listener.d.ts +0 -63
- package/dist/rules/webhook-listener.js +0 -224
- package/dist/rules/webhook-listener.js.map +0 -1
- package/dist/rules/webhook-token.d.ts +0 -50
- package/dist/rules/webhook-token.js +0 -91
- package/dist/rules/webhook-token.js.map +0 -1
- package/dist/schema/field-aliases.d.ts +0 -34
- package/dist/schema/field-aliases.js +0 -132
- package/dist/schema/field-aliases.js.map +0 -1
- package/dist/sinks/dispatcher.d.ts +0 -7
- package/dist/sinks/dispatcher.js +0 -13
- package/dist/sinks/dispatcher.js.map +0 -1
- package/dist/sinks/file.d.ts +0 -6
- package/dist/sinks/file.js +0 -20
- package/dist/sinks/file.js.map +0 -1
- package/dist/sinks/format.d.ts +0 -20
- package/dist/sinks/format.js +0 -57
- package/dist/sinks/format.js.map +0 -1
- package/dist/sinks/homeassistant.d.ts +0 -18
- package/dist/sinks/homeassistant.js +0 -45
- package/dist/sinks/homeassistant.js.map +0 -1
- package/dist/sinks/openclaw.d.ts +0 -13
- package/dist/sinks/openclaw.js +0 -34
- package/dist/sinks/openclaw.js.map +0 -1
- package/dist/sinks/stdout.d.ts +0 -4
- package/dist/sinks/stdout.js +0 -6
- package/dist/sinks/stdout.js.map +0 -1
- package/dist/sinks/telegram.d.ts +0 -11
- package/dist/sinks/telegram.js +0 -29
- package/dist/sinks/telegram.js.map +0 -1
- package/dist/sinks/types.d.ts +0 -13
- package/dist/sinks/types.js +0 -2
- package/dist/sinks/types.js.map +0 -1
- package/dist/sinks/webhook.d.ts +0 -6
- package/dist/sinks/webhook.js +0 -23
- package/dist/sinks/webhook.js.map +0 -1
- package/dist/status-sync/manager.d.ts +0 -48
- package/dist/status-sync/manager.js +0 -269
- package/dist/status-sync/manager.js.map +0 -1
- package/dist/utils/arg-parsers.d.ts +0 -16
- package/dist/utils/arg-parsers.js +0 -67
- package/dist/utils/arg-parsers.js.map +0 -1
- package/dist/utils/audit.d.ts +0 -69
- package/dist/utils/audit.js +0 -122
- package/dist/utils/audit.js.map +0 -1
- package/dist/utils/filter.d.ts +0 -81
- package/dist/utils/filter.js +0 -190
- package/dist/utils/filter.js.map +0 -1
- package/dist/utils/flags.d.ts +0 -72
- package/dist/utils/flags.js +0 -187
- package/dist/utils/flags.js.map +0 -1
- package/dist/utils/format.d.ts +0 -9
- package/dist/utils/format.js +0 -118
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/health.d.ts +0 -48
- package/dist/utils/health.js +0 -102
- package/dist/utils/health.js.map +0 -1
- package/dist/utils/help-json.d.ts +0 -39
- package/dist/utils/help-json.js +0 -55
- package/dist/utils/help-json.js.map +0 -1
- package/dist/utils/name-resolver.d.ts +0 -26
- package/dist/utils/name-resolver.js +0 -138
- package/dist/utils/name-resolver.js.map +0 -1
- package/dist/utils/output.d.ts +0 -73
- package/dist/utils/output.js +0 -405
- package/dist/utils/output.js.map +0 -1
- package/dist/utils/quota.d.ts +0 -61
- package/dist/utils/quota.js +0 -228
- package/dist/utils/quota.js.map +0 -1
- package/dist/utils/redact.d.ts +0 -23
- package/dist/utils/redact.js +0 -69
- package/dist/utils/redact.js.map +0 -1
- package/dist/utils/retry.d.ts +0 -72
- package/dist/utils/retry.js +0 -141
- package/dist/utils/retry.js.map +0 -1
- package/dist/utils/string.d.ts +0 -2
- package/dist/utils/string.js +0 -23
- package/dist/utils/string.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -5
- package/dist/version.js.map +0 -1
package/dist/rules/suggest.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { stringify as yamlStringify } from 'yaml';
|
|
2
|
-
import { COMMAND_KEYWORDS } from '../lib/command-keywords.js';
|
|
3
|
-
const TRIGGER_KEYWORDS = [
|
|
4
|
-
{ pattern: /\bmotion\b|\bdetect/i, trigger: 'mqtt', event: 'motion.detected' },
|
|
5
|
-
{ pattern: /\bdoor\b|\bcontact\b|\bopen.*sensor/i, trigger: 'mqtt', event: 'contact.opened' },
|
|
6
|
-
{ pattern: /\bbutton\b|\bpress/i, trigger: 'mqtt', event: 'button.pressed' },
|
|
7
|
-
{ pattern: /\bwebhook\b|\bhttp\b|\bifttt\b/i, trigger: 'webhook' },
|
|
8
|
-
{ pattern: /\bevery\b|\bdaily\b|\bmorning\b|\bnight\b|\bevening\b|\b\d{1,2}\s*[ap]m\b/i, trigger: 'cron' },
|
|
9
|
-
];
|
|
10
|
-
function inferTrigger(intent) {
|
|
11
|
-
for (const t of TRIGGER_KEYWORDS) {
|
|
12
|
-
if (t.pattern.test(intent))
|
|
13
|
-
return { trigger: t.trigger, event: t.event };
|
|
14
|
-
}
|
|
15
|
-
return { trigger: 'mqtt', event: 'device.shadow' };
|
|
16
|
-
}
|
|
17
|
-
function inferSchedule(intent, warnings) {
|
|
18
|
-
const amMatch = /\b(\d{1,2})\s*am\b/i.exec(intent);
|
|
19
|
-
if (amMatch)
|
|
20
|
-
return `0 ${parseInt(amMatch[1], 10)} * * *`;
|
|
21
|
-
const pmMatch = /\b(\d{1,2})\s*pm\b/i.exec(intent);
|
|
22
|
-
if (pmMatch)
|
|
23
|
-
return `0 ${parseInt(pmMatch[1], 10) + 12} * * *`;
|
|
24
|
-
if (/\bevery\s*hour/i.test(intent))
|
|
25
|
-
return '0 * * * *';
|
|
26
|
-
if (/\bnight\b|\bevening\b/i.test(intent))
|
|
27
|
-
return '0 22 * * *';
|
|
28
|
-
if (/\bmorning\b/i.test(intent))
|
|
29
|
-
return '0 8 * * *';
|
|
30
|
-
warnings.push(`Could not infer cron schedule from intent "${intent}" — defaulted to "0 8 * * *". Edit the generated rule to set the correct schedule.`);
|
|
31
|
-
return '0 8 * * *';
|
|
32
|
-
}
|
|
33
|
-
function inferCommand(intent, warnings) {
|
|
34
|
-
for (const k of COMMAND_KEYWORDS) {
|
|
35
|
-
if (k.pattern.test(intent))
|
|
36
|
-
return k.command;
|
|
37
|
-
}
|
|
38
|
-
warnings.push(`Could not infer command from intent "${intent}" — defaulted to "turnOn". Edit the generated rule to set the correct command.`);
|
|
39
|
-
return 'turnOn';
|
|
40
|
-
}
|
|
41
|
-
export function suggestRule(opts) {
|
|
42
|
-
const warnings = [];
|
|
43
|
-
// Resolve trigger
|
|
44
|
-
let triggerSource = opts.trigger;
|
|
45
|
-
let inferredEvent;
|
|
46
|
-
if (!triggerSource) {
|
|
47
|
-
const inferred = inferTrigger(opts.intent);
|
|
48
|
-
triggerSource = inferred.trigger;
|
|
49
|
-
inferredEvent = inferred.event;
|
|
50
|
-
if (inferredEvent === 'device.shadow') {
|
|
51
|
-
warnings.push(`Could not infer trigger type from intent "${opts.intent}" — defaulted to mqtt/device.shadow. Set --trigger and --event explicitly.`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Build the when block
|
|
55
|
-
let when;
|
|
56
|
-
if (triggerSource === 'mqtt') {
|
|
57
|
-
const event = opts.event ?? inferredEvent ?? 'device.shadow';
|
|
58
|
-
const mqttTrigger = { source: 'mqtt', event };
|
|
59
|
-
if (opts.devices && opts.devices.length > 0) {
|
|
60
|
-
const sensorDevice = opts.devices[0];
|
|
61
|
-
mqttTrigger.device = sensorDevice.name ?? sensorDevice.id;
|
|
62
|
-
}
|
|
63
|
-
when = mqttTrigger;
|
|
64
|
-
}
|
|
65
|
-
else if (triggerSource === 'cron') {
|
|
66
|
-
const schedule = opts.schedule ?? inferSchedule(opts.intent, warnings);
|
|
67
|
-
const cronTrigger = { source: 'cron', schedule };
|
|
68
|
-
if (opts.days && opts.days.length > 0)
|
|
69
|
-
cronTrigger.days = opts.days;
|
|
70
|
-
when = cronTrigger;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
when = { source: 'webhook', path: opts.webhookPath ?? '/action' };
|
|
74
|
-
}
|
|
75
|
-
// Build then[] — one action per device (skip the sensor device for mqtt)
|
|
76
|
-
const command = inferCommand(opts.intent, warnings);
|
|
77
|
-
const actionDevices = triggerSource === 'mqtt' && opts.devices && opts.devices.length > 1
|
|
78
|
-
? opts.devices.slice(1)
|
|
79
|
-
: (opts.devices ?? []);
|
|
80
|
-
const then = actionDevices.length > 0
|
|
81
|
-
? actionDevices.map((d) => ({
|
|
82
|
-
command: `devices command <id> ${command}`,
|
|
83
|
-
device: d.id,
|
|
84
|
-
}))
|
|
85
|
-
: [{ command: `devices command <id> ${command}` }];
|
|
86
|
-
const rule = {
|
|
87
|
-
name: opts.intent,
|
|
88
|
-
when,
|
|
89
|
-
then,
|
|
90
|
-
dry_run: true,
|
|
91
|
-
...(triggerSource === 'mqtt' ? { throttle: { max_per: '10m' } } : {}),
|
|
92
|
-
};
|
|
93
|
-
const ruleYaml = yamlStringify(rule, { lineWidth: 0 });
|
|
94
|
-
return { rule, ruleYaml, warnings };
|
|
95
|
-
}
|
|
96
|
-
//# sourceMappingURL=suggest.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/rules/suggest.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/throttle.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Throttle gate — per-rule, optionally keyed by deviceId.
|
|
3
|
-
*
|
|
4
|
-
* Semantics:
|
|
5
|
-
* - `max_per: "10m"` → a rule may fire at most once every 10 minutes
|
|
6
|
-
* per (rule, deviceId) pair.
|
|
7
|
-
* - `dedupe_window: "5s"` → suppress fires whose key already fired
|
|
8
|
-
* within the window (collapses rapid sensor bursts into one action).
|
|
9
|
-
* - Fires that would violate the window are **suppressed** (not
|
|
10
|
-
* queued) and surface as `{ allowed: false, reason: 'throttled' }`.
|
|
11
|
-
* - When a rule has no `throttle` block, `ThrottleGate.check` returns
|
|
12
|
-
* `{ allowed: true }` immediately.
|
|
13
|
-
*
|
|
14
|
-
* The gate is in-memory only. Re-reads between processes (or after
|
|
15
|
-
* SIGHUP reload) start with a clean slate — a deliberate choice,
|
|
16
|
-
* because persisting throttle state would lock the engine into a
|
|
17
|
-
* schema that changes every time we add a trigger type.
|
|
18
|
-
*/
|
|
19
|
-
export declare function parseMaxPerMs(expr: string): number;
|
|
20
|
-
export interface ThrottleCheckResult {
|
|
21
|
-
allowed: boolean;
|
|
22
|
-
/** Timestamp of the last fire that occupies the window, if any. */
|
|
23
|
-
lastFiredAt?: number;
|
|
24
|
-
/** When the window will reopen. */
|
|
25
|
-
nextAllowedAt?: number;
|
|
26
|
-
/** Whether this was blocked by the dedupe_window (vs max_per). */
|
|
27
|
-
dedupedBy?: 'dedupe_window' | 'max_per' | 'cooldown' | 'maxFiringsPerHour';
|
|
28
|
-
}
|
|
29
|
-
export interface MaxFiringsCheckResult {
|
|
30
|
-
allowed: boolean;
|
|
31
|
-
count: number;
|
|
32
|
-
max: number;
|
|
33
|
-
}
|
|
34
|
-
export declare class ThrottleGate {
|
|
35
|
-
private lastFireAt;
|
|
36
|
-
/** Sliding-window fire-time log for count-based maxFiringsPerHour. */
|
|
37
|
-
private fireTimes;
|
|
38
|
-
private keyOf;
|
|
39
|
-
/**
|
|
40
|
-
* Does **not** record the fire. Call `record()` after the action
|
|
41
|
-
* actually runs so that dry-run / throttled paths don't bump the
|
|
42
|
-
* window.
|
|
43
|
-
*/
|
|
44
|
-
check(ruleName: string, windowMs: number | null, now: number, deviceId?: string, dedupeWindowMs?: number | null): ThrottleCheckResult;
|
|
45
|
-
record(ruleName: string, now: number, deviceId?: string): void;
|
|
46
|
-
/** Count-based check: has the rule fired >= maxCount times in the last windowMs? */
|
|
47
|
-
checkMaxFirings(ruleName: string, maxCount: number, windowMs: number, now: number, deviceId?: string): MaxFiringsCheckResult;
|
|
48
|
-
/** Record a count-based fire (call alongside record()). */
|
|
49
|
-
recordFire(ruleName: string, now: number, deviceId?: string): void;
|
|
50
|
-
/** Drop everything — used by engine.reload when a rule is removed. */
|
|
51
|
-
forget(ruleName: string): void;
|
|
52
|
-
/**
|
|
53
|
-
* Drop every window whose rule name isn't in the given set — used by
|
|
54
|
-
* `engine.reload` after a policy swap. Entries for names that survive
|
|
55
|
-
* the reload are preserved so unchanged rules don't get a free
|
|
56
|
-
* one-fire amnesty.
|
|
57
|
-
*/
|
|
58
|
-
retainOnly(ruleNames: Set<string>): void;
|
|
59
|
-
/** Test helper — exposes the underlying size. */
|
|
60
|
-
size(): number;
|
|
61
|
-
}
|
package/dist/rules/throttle.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Throttle gate — per-rule, optionally keyed by deviceId.
|
|
3
|
-
*
|
|
4
|
-
* Semantics:
|
|
5
|
-
* - `max_per: "10m"` → a rule may fire at most once every 10 minutes
|
|
6
|
-
* per (rule, deviceId) pair.
|
|
7
|
-
* - `dedupe_window: "5s"` → suppress fires whose key already fired
|
|
8
|
-
* within the window (collapses rapid sensor bursts into one action).
|
|
9
|
-
* - Fires that would violate the window are **suppressed** (not
|
|
10
|
-
* queued) and surface as `{ allowed: false, reason: 'throttled' }`.
|
|
11
|
-
* - When a rule has no `throttle` block, `ThrottleGate.check` returns
|
|
12
|
-
* `{ allowed: true }` immediately.
|
|
13
|
-
*
|
|
14
|
-
* The gate is in-memory only. Re-reads between processes (or after
|
|
15
|
-
* SIGHUP reload) start with a clean slate — a deliberate choice,
|
|
16
|
-
* because persisting throttle state would lock the engine into a
|
|
17
|
-
* schema that changes every time we add a trigger type.
|
|
18
|
-
*/
|
|
19
|
-
const DURATION_RE = /^(\d+)([smh])$/;
|
|
20
|
-
export function parseMaxPerMs(expr) {
|
|
21
|
-
const m = DURATION_RE.exec(expr.trim());
|
|
22
|
-
if (!m)
|
|
23
|
-
throw new Error(`Invalid throttle.max_per: "${expr}"`);
|
|
24
|
-
const n = Number(m[1]);
|
|
25
|
-
const unit = m[2];
|
|
26
|
-
const unitMs = unit === 's' ? 1_000 : unit === 'm' ? 60_000 : 3_600_000;
|
|
27
|
-
return n * unitMs;
|
|
28
|
-
}
|
|
29
|
-
export class ThrottleGate {
|
|
30
|
-
lastFireAt = new Map();
|
|
31
|
-
/** Sliding-window fire-time log for count-based maxFiringsPerHour. */
|
|
32
|
-
fireTimes = new Map();
|
|
33
|
-
keyOf(ruleName, deviceId) {
|
|
34
|
-
return deviceId ? `${ruleName}::${deviceId}` : ruleName;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Does **not** record the fire. Call `record()` after the action
|
|
38
|
-
* actually runs so that dry-run / throttled paths don't bump the
|
|
39
|
-
* window.
|
|
40
|
-
*/
|
|
41
|
-
check(ruleName, windowMs, now, deviceId, dedupeWindowMs) {
|
|
42
|
-
const key = this.keyOf(ruleName, deviceId);
|
|
43
|
-
const last = this.lastFireAt.get(key);
|
|
44
|
-
// dedupe_window check: suppress if last fire was within this (typically smaller) window
|
|
45
|
-
if (dedupeWindowMs !== null && dedupeWindowMs !== undefined && dedupeWindowMs > 0) {
|
|
46
|
-
if (last !== undefined) {
|
|
47
|
-
const dedupeEnd = last + dedupeWindowMs;
|
|
48
|
-
if (now < dedupeEnd) {
|
|
49
|
-
return { allowed: false, lastFiredAt: last, nextAllowedAt: dedupeEnd, dedupedBy: 'dedupe_window' };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// max_per / cooldown check
|
|
54
|
-
if (windowMs === null || windowMs <= 0)
|
|
55
|
-
return { allowed: true, lastFiredAt: last };
|
|
56
|
-
if (last === undefined)
|
|
57
|
-
return { allowed: true };
|
|
58
|
-
const earliest = last + windowMs;
|
|
59
|
-
if (now >= earliest)
|
|
60
|
-
return { allowed: true, lastFiredAt: last };
|
|
61
|
-
return { allowed: false, lastFiredAt: last, nextAllowedAt: earliest, dedupedBy: windowMs > 0 ? 'max_per' : undefined };
|
|
62
|
-
}
|
|
63
|
-
record(ruleName, now, deviceId) {
|
|
64
|
-
this.lastFireAt.set(this.keyOf(ruleName, deviceId), now);
|
|
65
|
-
}
|
|
66
|
-
/** Count-based check: has the rule fired >= maxCount times in the last windowMs? */
|
|
67
|
-
checkMaxFirings(ruleName, maxCount, windowMs, now, deviceId) {
|
|
68
|
-
const key = this.keyOf(ruleName, deviceId);
|
|
69
|
-
const times = (this.fireTimes.get(key) ?? []).filter((t) => now - t < windowMs);
|
|
70
|
-
this.fireTimes.set(key, times);
|
|
71
|
-
return { allowed: times.length < maxCount, count: times.length, max: maxCount };
|
|
72
|
-
}
|
|
73
|
-
/** Record a count-based fire (call alongside record()). */
|
|
74
|
-
recordFire(ruleName, now, deviceId) {
|
|
75
|
-
const key = this.keyOf(ruleName, deviceId);
|
|
76
|
-
const times = this.fireTimes.get(key) ?? [];
|
|
77
|
-
times.push(now);
|
|
78
|
-
this.fireTimes.set(key, times);
|
|
79
|
-
}
|
|
80
|
-
/** Drop everything — used by engine.reload when a rule is removed. */
|
|
81
|
-
forget(ruleName) {
|
|
82
|
-
const prefix = `${ruleName}::`;
|
|
83
|
-
for (const k of this.lastFireAt.keys()) {
|
|
84
|
-
if (k === ruleName || k.startsWith(prefix))
|
|
85
|
-
this.lastFireAt.delete(k);
|
|
86
|
-
}
|
|
87
|
-
for (const k of this.fireTimes.keys()) {
|
|
88
|
-
if (k === ruleName || k.startsWith(prefix))
|
|
89
|
-
this.fireTimes.delete(k);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Drop every window whose rule name isn't in the given set — used by
|
|
94
|
-
* `engine.reload` after a policy swap. Entries for names that survive
|
|
95
|
-
* the reload are preserved so unchanged rules don't get a free
|
|
96
|
-
* one-fire amnesty.
|
|
97
|
-
*/
|
|
98
|
-
retainOnly(ruleNames) {
|
|
99
|
-
for (const k of this.lastFireAt.keys()) {
|
|
100
|
-
const sep = k.indexOf('::');
|
|
101
|
-
const ruleName = sep === -1 ? k : k.slice(0, sep);
|
|
102
|
-
if (!ruleNames.has(ruleName))
|
|
103
|
-
this.lastFireAt.delete(k);
|
|
104
|
-
}
|
|
105
|
-
for (const k of this.fireTimes.keys()) {
|
|
106
|
-
const sep = k.indexOf('::');
|
|
107
|
-
const ruleName = sep === -1 ? k : k.slice(0, sep);
|
|
108
|
-
if (!ruleNames.has(ruleName))
|
|
109
|
-
this.fireTimes.delete(k);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/** Test helper — exposes the underlying size. */
|
|
113
|
-
size() {
|
|
114
|
-
return this.lastFireAt.size;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
//# sourceMappingURL=throttle.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"throttle.js","sourceRoot":"","sources":["../../src/rules/throttle.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/types.d.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime TypeScript shapes for policy v0.2 rule objects.
|
|
3
|
-
*
|
|
4
|
-
* These are hand-mirrored from `src/policy/schema/v0.2.json` — the ajv
|
|
5
|
-
* validator is the source of truth for what a file may contain, this
|
|
6
|
-
* file is the source of truth for what the engine expects after load.
|
|
7
|
-
* When you edit one, edit the other in the same commit.
|
|
8
|
-
*/
|
|
9
|
-
import type { DestructiveCommand } from './destructive.js';
|
|
10
|
-
export type TriggerSource = 'mqtt' | 'cron' | 'webhook';
|
|
11
|
-
export interface MqttTrigger {
|
|
12
|
-
source: 'mqtt';
|
|
13
|
-
/**
|
|
14
|
-
* Event name matched against the engine's event classifier. Known
|
|
15
|
-
* values today: `device.shadow` (catch-all), `motion.detected`,
|
|
16
|
-
* `motion.cleared`, `contact.opened`, `contact.closed`.
|
|
17
|
-
*/
|
|
18
|
-
event: string;
|
|
19
|
-
/** Optional filter by deviceId or alias. */
|
|
20
|
-
device?: string;
|
|
21
|
-
}
|
|
22
|
-
export type DayOfWeek = 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday';
|
|
23
|
-
export interface CronTrigger {
|
|
24
|
-
source: 'cron';
|
|
25
|
-
/** Standard 5-field cron (minute hour dom month dow), local tz. */
|
|
26
|
-
schedule: string;
|
|
27
|
-
/**
|
|
28
|
-
* Optional weekday filter applied AFTER the cron expression fires.
|
|
29
|
-
* When omitted, every firing passes. Values are matched
|
|
30
|
-
* case-insensitively against the local weekday name.
|
|
31
|
-
*/
|
|
32
|
-
days?: DayOfWeek[];
|
|
33
|
-
}
|
|
34
|
-
export interface WebhookTrigger {
|
|
35
|
-
source: 'webhook';
|
|
36
|
-
/** Local HTTP path the rule engine listens on, e.g. `/kitchen/motion`. */
|
|
37
|
-
path: string;
|
|
38
|
-
}
|
|
39
|
-
export type Trigger = MqttTrigger | CronTrigger | WebhookTrigger;
|
|
40
|
-
export interface TimeBetweenCondition {
|
|
41
|
-
time_between: [string, string];
|
|
42
|
-
}
|
|
43
|
-
export interface DeviceStateCondition {
|
|
44
|
-
device: string;
|
|
45
|
-
field: string;
|
|
46
|
-
op: '==' | '!=' | '<' | '>' | '<=' | '>=';
|
|
47
|
-
value: unknown;
|
|
48
|
-
}
|
|
49
|
-
export interface AllCondition {
|
|
50
|
-
all: Condition[];
|
|
51
|
-
}
|
|
52
|
-
export interface AnyCondition {
|
|
53
|
-
any: Condition[];
|
|
54
|
-
}
|
|
55
|
-
export interface NotCondition {
|
|
56
|
-
not: Condition;
|
|
57
|
-
}
|
|
58
|
-
export type Condition = TimeBetweenCondition | DeviceStateCondition | AllCondition | AnyCondition | NotCondition;
|
|
59
|
-
export interface Action {
|
|
60
|
-
command: string;
|
|
61
|
-
device?: string;
|
|
62
|
-
args?: Record<string, unknown> | null;
|
|
63
|
-
on_error?: 'continue' | 'stop';
|
|
64
|
-
}
|
|
65
|
-
export interface Throttle {
|
|
66
|
-
max_per: string;
|
|
67
|
-
/** Deduplicate identical events arriving within this window after the last fire. */
|
|
68
|
-
dedupe_window?: string;
|
|
69
|
-
}
|
|
70
|
-
export interface Rule {
|
|
71
|
-
name: string;
|
|
72
|
-
enabled?: boolean;
|
|
73
|
-
when: Trigger;
|
|
74
|
-
conditions?: Condition[] | null;
|
|
75
|
-
then: Action[];
|
|
76
|
-
throttle?: Throttle | null;
|
|
77
|
-
dry_run?: boolean;
|
|
78
|
-
/** Shorthand cooldown — equivalent to throttle.max_per. Takes precedence when both are set. */
|
|
79
|
-
cooldown?: string;
|
|
80
|
-
/** Hysteresis guard — device state must remain stable for this duration before the rule fires. */
|
|
81
|
-
requires_stable_for?: string;
|
|
82
|
-
/** Alias for requires_stable_for with clearer semantics (takes precedence when both are set). */
|
|
83
|
-
hysteresis?: string;
|
|
84
|
-
/** Maximum times this rule may fire per rolling hour (count-based rate limit). */
|
|
85
|
-
maxFiringsPerHour?: number;
|
|
86
|
-
/** Skip the action if the device's last-known cached state already matches the desired command outcome. */
|
|
87
|
-
suppressIfAlreadyDesired?: boolean;
|
|
88
|
-
}
|
|
89
|
-
export interface AutomationBlock {
|
|
90
|
-
enabled?: boolean;
|
|
91
|
-
rules?: Rule[] | null;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Engine event — unified shape the matcher consumes regardless of
|
|
95
|
-
* trigger source.
|
|
96
|
-
*/
|
|
97
|
-
export interface EngineEvent {
|
|
98
|
-
source: TriggerSource;
|
|
99
|
-
/** Classifier output for MQTT; schedule string for cron; path for webhook. */
|
|
100
|
-
event: string;
|
|
101
|
-
t: Date;
|
|
102
|
-
/** Resolved deviceId if the trigger carried one (MQTT). */
|
|
103
|
-
deviceId?: string;
|
|
104
|
-
/** Raw trigger payload for inspection / audit. */
|
|
105
|
-
payload?: unknown;
|
|
106
|
-
}
|
|
107
|
-
/** Guards used outside this file. */
|
|
108
|
-
export declare function isMqttTrigger(t: Trigger): t is MqttTrigger;
|
|
109
|
-
export declare function isCronTrigger(t: Trigger): t is CronTrigger;
|
|
110
|
-
export declare function isWebhookTrigger(t: Trigger): t is WebhookTrigger;
|
|
111
|
-
export declare function isTimeBetween(c: Condition): c is TimeBetweenCondition;
|
|
112
|
-
export declare function isDeviceState(c: Condition): c is DeviceStateCondition;
|
|
113
|
-
export declare function isAllCondition(c: Condition): c is AllCondition;
|
|
114
|
-
export declare function isAnyCondition(c: Condition): c is AnyCondition;
|
|
115
|
-
export declare function isNotCondition(c: Condition): c is NotCondition;
|
|
116
|
-
/** Re-export for consumers that want the single list without a second import. */
|
|
117
|
-
export type { DestructiveCommand };
|
package/dist/rules/types.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime TypeScript shapes for policy v0.2 rule objects.
|
|
3
|
-
*
|
|
4
|
-
* These are hand-mirrored from `src/policy/schema/v0.2.json` — the ajv
|
|
5
|
-
* validator is the source of truth for what a file may contain, this
|
|
6
|
-
* file is the source of truth for what the engine expects after load.
|
|
7
|
-
* When you edit one, edit the other in the same commit.
|
|
8
|
-
*/
|
|
9
|
-
/** Guards used outside this file. */
|
|
10
|
-
export function isMqttTrigger(t) {
|
|
11
|
-
return t.source === 'mqtt';
|
|
12
|
-
}
|
|
13
|
-
export function isCronTrigger(t) {
|
|
14
|
-
return t.source === 'cron';
|
|
15
|
-
}
|
|
16
|
-
export function isWebhookTrigger(t) {
|
|
17
|
-
return t.source === 'webhook';
|
|
18
|
-
}
|
|
19
|
-
export function isTimeBetween(c) {
|
|
20
|
-
return Array.isArray(c.time_between);
|
|
21
|
-
}
|
|
22
|
-
export function isDeviceState(c) {
|
|
23
|
-
const d = c;
|
|
24
|
-
return typeof d.device === 'string' && typeof d.field === 'string' && typeof d.op === 'string';
|
|
25
|
-
}
|
|
26
|
-
export function isAllCondition(c) {
|
|
27
|
-
return Array.isArray(c.all);
|
|
28
|
-
}
|
|
29
|
-
export function isAnyCondition(c) {
|
|
30
|
-
return Array.isArray(c.any);
|
|
31
|
-
}
|
|
32
|
-
export function isNotCondition(c) {
|
|
33
|
-
return c.not !== undefined && !Array.isArray(c.not);
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=types.js.map
|
package/dist/rules/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuHH,qCAAqC;AACrC,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,OAAO,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;AAC7B,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,OAAO,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;AAC7B,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,CAAU;IACzC,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;AAChC,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,CAAY;IACxC,OAAO,KAAK,CAAC,OAAO,CAAE,CAA0B,CAAC,YAAY,CAAC,CAAC;AACjE,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,CAAY;IACxC,MAAM,CAAC,GAAG,CAAyB,CAAC;IACpC,OAAO,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC;AACjG,CAAC;AACD,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,OAAO,KAAK,CAAC,OAAO,CAAE,CAAkB,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AACD,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,OAAO,KAAK,CAAC,OAAO,CAAE,CAAkB,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AACD,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,OAAQ,CAAkB,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAE,CAAkB,CAAC,GAAG,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local HTTP listener that delivers webhook events to the rules engine.
|
|
3
|
-
*
|
|
4
|
-
* Scope (E2):
|
|
5
|
-
* - Binds to `127.0.0.1` only — the loopback interface keeps the
|
|
6
|
-
* listener off the network by default. The plan's integration story
|
|
7
|
-
* is that an agent or local script POSTs to this endpoint.
|
|
8
|
-
* - Default port is 18790 (phase-3 design doc choice); override with
|
|
9
|
-
* `--webhook-port <n>` in `switchbot rules run`. `--webhook-port 0`
|
|
10
|
-
* asks the OS for an ephemeral port — useful in tests.
|
|
11
|
-
* - Bearer-token auth on every request: `Authorization: Bearer <t>`.
|
|
12
|
-
* The expected token comes from `WebhookTokenStore`; unauthorized
|
|
13
|
-
* requests get a 401 with no body, no hint about which header
|
|
14
|
-
* failed, and an audit entry (`rule-webhook-rejected`).
|
|
15
|
-
* - Matches request path against registered webhook rules: only
|
|
16
|
-
* `POST /path/exactly/as/declared`. Unknown paths return 404.
|
|
17
|
-
*
|
|
18
|
-
* Non-goals:
|
|
19
|
-
* - No TLS; operators who expose this outside loopback are expected
|
|
20
|
-
* to sit behind a reverse proxy that terminates TLS.
|
|
21
|
-
* - No payload parsing beyond reading the body as a string — the
|
|
22
|
-
* engine passes the raw body through in the event payload.
|
|
23
|
-
*/
|
|
24
|
-
import type { EngineEvent, Rule } from './types.js';
|
|
25
|
-
export declare const DEFAULT_WEBHOOK_PORT = 18790;
|
|
26
|
-
export interface WebhookDispatch {
|
|
27
|
-
(rule: Rule, event: EngineEvent): Promise<void>;
|
|
28
|
-
}
|
|
29
|
-
export interface WebhookListenerOptions {
|
|
30
|
-
rules: Rule[];
|
|
31
|
-
/** Bearer token used to authorize incoming requests. */
|
|
32
|
-
bearerToken: string;
|
|
33
|
-
/**
|
|
34
|
-
* Host interface to bind. Defaults to 127.0.0.1; tests can set this
|
|
35
|
-
* to '127.0.0.1' + port 0 for ephemeral allocation.
|
|
36
|
-
*/
|
|
37
|
-
host?: string;
|
|
38
|
-
port?: number;
|
|
39
|
-
dispatch: WebhookDispatch;
|
|
40
|
-
/** Optional clock — tests inject a deterministic value. */
|
|
41
|
-
now?: () => Date;
|
|
42
|
-
}
|
|
43
|
-
export declare class WebhookListener {
|
|
44
|
-
private readonly opts;
|
|
45
|
-
private server;
|
|
46
|
-
private readonly pathIndex;
|
|
47
|
-
private actualPort;
|
|
48
|
-
constructor(opts: WebhookListenerOptions);
|
|
49
|
-
/** Start listening. Resolves once the server has bound a port. */
|
|
50
|
-
start(): Promise<void>;
|
|
51
|
-
stop(): Promise<void>;
|
|
52
|
-
getPort(): number | null;
|
|
53
|
-
listPaths(): string[];
|
|
54
|
-
/**
|
|
55
|
-
* Replace the current rule → path index. Used by `engine.reload`: the
|
|
56
|
-
* listener keeps its open port and accepted connections, but routes
|
|
57
|
-
* subsequent requests against the fresh policy.
|
|
58
|
-
*/
|
|
59
|
-
updateRules(rules: Rule[]): void;
|
|
60
|
-
private handle;
|
|
61
|
-
private isAuthorized;
|
|
62
|
-
private now;
|
|
63
|
-
}
|