-
Notifications
You must be signed in to change notification settings - Fork 207
feat(cli): add --channel-plugin flag to load a channel from an extern… #546
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
87d138c
a964b36
ea6ea08
4d69188
b26f156
12bca47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| const { execSync } = require('child_process'); | ||
|
|
||
| try { | ||
| const output = execSync('npx vitest run --coverage', { encoding: 'utf8', stdio: 'pipe' }); | ||
| console.log(output); | ||
| } catch (error) { | ||
| console.log(error.stdout); | ||
| console.log(error.stderr); | ||
|
|
||
| // Check if coverage was generated despite test failures | ||
| if (error.stdout.includes('Coverage for branches')) { | ||
| const branchMatch = error.stdout.match(/Coverage for branches \(([0-9.]+)%\)/); | ||
| if (branchMatch) { | ||
| console.log(`\n=== BRANCH COVERAGE: ${branchMatch[1]}% ===`); | ||
| } | ||
| } | ||
|
|
||
| if (error.stderr.includes('ERROR: Coverage for branches')) { | ||
| console.log('\n=== COVERAGE ERROR FOUND ==='); | ||
| } | ||
| } |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
|
|
||
| [1m[30m[46m RUN [49m[39m[22m [36mv4.1.10 [39m[90mc:/Users/DELL/sorokeep[39m | ||
| [2mCoverage enabled with [22m[33mv8[39m | ||
|
|
||
| [31m❯[39m src/alerts/alerts.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/channels.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/discord.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/lib.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/instance_scan.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/keys.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/pagerduty.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/resource.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/simulation_cache.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/telegram.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m src/alerts/webhook.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/action/ttl-check-action.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/builtins.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/dispatcher.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/pagerduty_custom_templates.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/registry.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/lifecycle.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/slack.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/slack_custom_templates.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/state_change_alerts.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/template_context.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/templates.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/alerts/webhook.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/cli/schemaFormatter.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/alerts.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/alerts_plugin_channel.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/budget.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/channel_plugin_flag.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/check.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/channels.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/completion.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/completion_command.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/costs.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/db.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/daemon.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/db_branches.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/history.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/guard.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/inspect.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/pause.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/resources.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/status.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/aws_secrets.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/restore.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/commands/watch.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/budget.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/budget_progress.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/budget_enforcement.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/channels.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/check.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/costs.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/decoder.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/discovery.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/extension.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/inspect.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/introspection_cache.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/monitor.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/rent_projection.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/rate_limiter.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/state_diff.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/scvalTranslator.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/status.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/vault.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/core/watch.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/daemon/introspection.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docker/devnet-compose.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/daemon/loop.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docker/docker-compose.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docker/docker.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/alert_delivery.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/backup.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/budget_tracking.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/channel_type_migration.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/database.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/introspection_cache.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/migrator.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/rate_limiter.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/repositories.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/db/resource_usage_logs.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docs/cicd.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docs/get_monitored_keys_spec.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/docs/onboarding.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/logging/index.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/e2e/daemon-execution.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/e2e/sandbox-network.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/logging/logger.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/mcp/get_contract_status.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/mcp/get-extension-costs.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/mcp/lifecycle.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/mcp/server.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/rpc/client.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/systemd/service.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/rpc/resource_estimate.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/utils/formatting.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/utils/config.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
| [31m❯[39m tests/utils/watch-config.test.ts [2m([22m[2m0 test[22m[2m)[22m | ||
|
|
||
| [2m Test Files [22m [1m[31m96 failed[39m[22m[90m (96)[39m | ||
| [2m Tests [22m [2mno tests[22m | ||
| [2m Start at [22m 07:14:59 | ||
| [2m Duration [22m 74.21s[2m (transform 8.39s, setup 0ms, import 0ms, tests 0ms, environment 46ms)[22m | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,7 +46,7 @@ Look at `alerts/webhook.ts` or `alerts/pagerduty.ts` for the timeout/AbortContro | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 2. Register a `ChannelDefinition` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Add your channel to `src/alerts/builtins.ts` if it's shipping in sorokeep itself, or register it from your own application's startup code if you're embedding sorokeep as a library (`import { registerAlertChannel } from "sorokeep"` — see `src/lib.ts`). | ||||||||||||||||||||||||||||||
| Add your channel to `src/alerts/builtins.ts` if it's shipping in sorokeep itself, register it from your own application's startup code if you're embedding sorokeep as a library (`import { registerAlertChannel } from "sorokeep"` — see `src/lib.ts`), or publish it as an external npm package and load it with `--channel-plugin`. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||
| import { registerAlertChannel } from "./registry.js"; | ||||||||||||||||||||||||||||||
|
|
@@ -73,18 +73,59 @@ Field-by-field: | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| If you added it to `builtins.ts`, that's it — `registerBuiltinChannels()` is called once (idempotently) from both `dispatcher.ts` and `commands/alerts.ts`, so your channel is live everywhere. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 3. What you did *not* need to touch | ||||||||||||||||||||||||||||||
| ## 3. External plugin package convention | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| If you want a channel to be installable without changing sorokeep's source, publish a package whose default export is a registration function. The CLI loads it with `--channel-plugin <package>` and passes in sorokeep's public `registerAlertChannel` function. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||
| // package entrypoint, e.g. src/index.ts in your npm package | ||||||||||||||||||||||||||||||
| export default function registerMatrixChannel( | ||||||||||||||||||||||||||||||
| registerAlertChannel: typeof import("sorokeep").registerAlertChannel, | ||||||||||||||||||||||||||||||
| ): void { | ||||||||||||||||||||||||||||||
| registerAlertChannel({ | ||||||||||||||||||||||||||||||
| name: "matrix", | ||||||||||||||||||||||||||||||
| channel: { send: (target, event) => sendMatrixAlert(target, event) }, | ||||||||||||||||||||||||||||||
| targetOption: "url", | ||||||||||||||||||||||||||||||
| missingTargetError: "Error: --url is required when --type is matrix.", | ||||||||||||||||||||||||||||||
| supportsSigning: false, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
Comment on lines
+80
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Import the sender in the external-plugin example.
Proposed fix // package entrypoint, e.g. src/index.ts in your npm package
+import { sendMatrixAlert } from "./matrix.js";
+
export default function registerMatrixChannel(📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Minimal package metadata: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ```json | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| "name": "sorokeep-alert-channel-matrix", | ||||||||||||||||||||||||||||||
| "type": "module", | ||||||||||||||||||||||||||||||
| "exports": "./dist/index.js" | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Example CLI usage after `npm install` or a local link: | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||
| sorokeep --channel-plugin sorokeep-alert-channel-matrix alerts add \ | ||||||||||||||||||||||||||||||
| --contract <contractId> \ | ||||||||||||||||||||||||||||||
| --type matrix \ | ||||||||||||||||||||||||||||||
| --url '!room:example.org' \ | ||||||||||||||||||||||||||||||
| --threshold 1000 | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| The same flag also applies to long-running processes such as `sorokeep daemon`, so the daemon can deliver alerts through channels that were registered by external packages before it starts processing contracts. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 4. What you did *not* need to touch | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - `alerts/dispatcher.ts` — its default channel map is built from the registry (`listAlertChannels()`), not a hardcoded object. | ||||||||||||||||||||||||||||||
| - `commands/alerts.ts` — `alerts add --type matrix ...` resolves your `ChannelDefinition` from the registry; the target flag, error message, and signing behavior all come from what you registered. | ||||||||||||||||||||||||||||||
| - `db/schema.sql` — `channel_type` is a plain `TEXT` column with a non-empty check, not a fixed SQL enum. No migration needed. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 4. Tests | ||||||||||||||||||||||||||||||
| ## 5. Tests | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Follow the pattern in `tests/alerts/builtins.test.ts`: mock the underlying module (e.g. `vi.mock("../../src/alerts/matrix.js", ...)`), then assert the registered `channel.send` delegates to it with the right arguments. Add a contract-shaped test the way `tests/db/repositories.test.ts`'s `"accepts discord as a valid channel_type"` tests do, if your channel needs any DB-level exercise beyond what's already generic. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Per [CONTRIBUTING.md](../CONTRIBUTING.md#test-driven-development), write the test first. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 5. Docs | ||||||||||||||||||||||||||||||
| ## 6. Docs | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Add your channel to the table in `README.md`'s Alerting section, and to `sorokeep alerts add`'s `--type` help text in the same file. | ||||||||||||||||||||||||||||||
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Resolve the failed test run before committing coverage results.
This report records 96 failed suites and zero executed tests, so it cannot demonstrate coverage or a passing CI state. Fix the underlying suite-loading failure, then regenerate the report.
🤖 Prompt for AI Agents