@elnora-ai/linear 1.0.1 → 2.0.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/.claude-plugin/marketplace.json +7 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -1
- package/README.md +275 -25
- package/agents/linear-issue-creator.md +135 -17
- package/agents/linear-issue-reviewer.md +122 -23
- package/agents/linear-issue-updater.md +137 -25
- package/agents/linear-state-curator.md +173 -0
- package/agents/linear-url-to-issues.md +190 -26
- package/commands/linear-cleanup.md +64 -29
- package/dist/cli.js +69 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/auth.d.ts +10 -0
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +50 -3
- package/dist/client/auth.js.map +1 -1
- package/dist/client/linear-client.d.ts +7 -0
- package/dist/client/linear-client.d.ts.map +1 -1
- package/dist/client/linear-client.js +13 -1
- package/dist/client/linear-client.js.map +1 -1
- package/dist/commands/agent-activities.d.ts +3 -0
- package/dist/commands/agent-activities.d.ts.map +1 -0
- package/dist/commands/agent-activities.js +144 -0
- package/dist/commands/agent-activities.js.map +1 -0
- package/dist/commands/agent-sessions.d.ts +3 -0
- package/dist/commands/agent-sessions.d.ts.map +1 -0
- package/dist/commands/agent-sessions.js +132 -0
- package/dist/commands/agent-sessions.js.map +1 -0
- package/dist/commands/attachments.d.ts +3 -0
- package/dist/commands/attachments.d.ts.map +1 -0
- package/dist/commands/attachments.js +265 -0
- package/dist/commands/attachments.js.map +1 -0
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +73 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/comments.d.ts +3 -0
- package/dist/commands/comments.d.ts.map +1 -0
- package/dist/commands/comments.js +107 -0
- package/dist/commands/comments.js.map +1 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +62 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +94 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/curator.d.ts +14 -0
- package/dist/commands/curator.d.ts.map +1 -1
- package/dist/commands/curator.js +97 -19
- package/dist/commands/curator.js.map +1 -1
- package/dist/commands/customer-needs.d.ts +3 -0
- package/dist/commands/customer-needs.d.ts.map +1 -0
- package/dist/commands/customer-needs.js +198 -0
- package/dist/commands/customer-needs.js.map +1 -0
- package/dist/commands/customers.d.ts +5 -0
- package/dist/commands/customers.d.ts.map +1 -0
- package/dist/commands/customers.js +201 -0
- package/dist/commands/customers.js.map +1 -0
- package/dist/commands/cycles.d.ts +3 -0
- package/dist/commands/cycles.d.ts.map +1 -0
- package/dist/commands/cycles.js +67 -0
- package/dist/commands/cycles.js.map +1 -0
- package/dist/commands/documents.d.ts +3 -0
- package/dist/commands/documents.d.ts.map +1 -0
- package/dist/commands/documents.js +105 -0
- package/dist/commands/documents.js.map +1 -0
- package/dist/commands/favorites.d.ts +3 -0
- package/dist/commands/favorites.d.ts.map +1 -0
- package/dist/commands/favorites.js +101 -0
- package/dist/commands/favorites.js.map +1 -0
- package/dist/commands/index.d.ts +30 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +30 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/initiatives.d.ts +3 -0
- package/dist/commands/initiatives.d.ts.map +1 -0
- package/dist/commands/initiatives.js +106 -0
- package/dist/commands/initiatives.js.map +1 -0
- package/dist/commands/issues.d.ts +21 -0
- package/dist/commands/issues.d.ts.map +1 -0
- package/dist/commands/issues.js +1083 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/labels.d.ts +3 -0
- package/dist/commands/labels.d.ts.map +1 -0
- package/dist/commands/labels.js +111 -0
- package/dist/commands/labels.js.map +1 -0
- package/dist/commands/milestones.d.ts +3 -0
- package/dist/commands/milestones.d.ts.map +1 -0
- package/dist/commands/milestones.js +94 -0
- package/dist/commands/milestones.js.map +1 -0
- package/dist/commands/notifications.d.ts +3 -0
- package/dist/commands/notifications.d.ts.map +1 -0
- package/dist/commands/notifications.js +130 -0
- package/dist/commands/notifications.js.map +1 -0
- package/dist/commands/project-labels.d.ts +3 -0
- package/dist/commands/project-labels.d.ts.map +1 -0
- package/dist/commands/project-labels.js +80 -0
- package/dist/commands/project-labels.js.map +1 -0
- package/dist/commands/project-relations.d.ts +3 -0
- package/dist/commands/project-relations.d.ts.map +1 -0
- package/dist/commands/project-relations.js +96 -0
- package/dist/commands/project-relations.js.map +1 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +263 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/quota.d.ts +3 -0
- package/dist/commands/quota.d.ts.map +1 -0
- package/dist/commands/quota.js +28 -0
- package/dist/commands/quota.js.map +1 -0
- package/dist/commands/reactions.d.ts +7 -0
- package/dist/commands/reactions.d.ts.map +1 -0
- package/dist/commands/reactions.js +53 -0
- package/dist/commands/reactions.js.map +1 -0
- package/dist/commands/relations.d.ts +3 -0
- package/dist/commands/relations.d.ts.map +1 -0
- package/dist/commands/relations.js +73 -0
- package/dist/commands/relations.js.map +1 -0
- package/dist/commands/states.d.ts +3 -0
- package/dist/commands/states.d.ts.map +1 -0
- package/dist/commands/states.js +52 -0
- package/dist/commands/states.js.map +1 -0
- package/dist/commands/status-updates.d.ts +3 -0
- package/dist/commands/status-updates.d.ts.map +1 -0
- package/dist/commands/status-updates.js +117 -0
- package/dist/commands/status-updates.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +58 -18
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/teams.d.ts +3 -0
- package/dist/commands/teams.d.ts.map +1 -0
- package/dist/commands/teams.js +135 -0
- package/dist/commands/teams.js.map +1 -0
- package/dist/commands/templates.d.ts +3 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/templates.js +76 -0
- package/dist/commands/templates.js.map +1 -0
- package/dist/commands/users.d.ts +3 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +40 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/views.d.ts +3 -0
- package/dist/commands/views.d.ts.map +1 -0
- package/dist/commands/views.js +177 -0
- package/dist/commands/views.js.map +1 -0
- package/dist/commands/webhooks.d.ts +3 -0
- package/dist/commands/webhooks.d.ts.map +1 -0
- package/dist/commands/webhooks.js +234 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +3 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +15 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/curator/dispatch.d.ts +52 -0
- package/dist/curator/dispatch.d.ts.map +1 -0
- package/dist/curator/dispatch.js +144 -0
- package/dist/curator/dispatch.js.map +1 -0
- package/dist/curator/index.d.ts +5 -0
- package/dist/curator/index.d.ts.map +1 -0
- package/dist/curator/index.js +5 -0
- package/dist/curator/index.js.map +1 -0
- package/dist/curator/llm.d.ts +70 -0
- package/dist/curator/llm.d.ts.map +1 -0
- package/dist/curator/llm.js +107 -0
- package/dist/curator/llm.js.map +1 -0
- package/dist/curator/snapshot.d.ts +34 -0
- package/dist/curator/snapshot.d.ts.map +1 -0
- package/dist/curator/snapshot.js +127 -0
- package/dist/curator/snapshot.js.map +1 -0
- package/dist/curator/state.d.ts +50 -0
- package/dist/curator/state.d.ts.map +1 -0
- package/dist/curator/state.js +125 -0
- package/dist/curator/state.js.map +1 -0
- package/dist/lib/bulk-graphql.d.ts +144 -0
- package/dist/lib/bulk-graphql.d.ts.map +1 -0
- package/dist/lib/bulk-graphql.js +380 -0
- package/dist/lib/bulk-graphql.js.map +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/output/cli.d.ts +17 -0
- package/dist/output/cli.d.ts.map +1 -0
- package/dist/output/cli.js +252 -0
- package/dist/output/cli.js.map +1 -0
- package/dist/output/formatter.d.ts +6 -0
- package/dist/output/formatter.d.ts.map +1 -1
- package/dist/output/formatter.js +10 -0
- package/dist/output/formatter.js.map +1 -1
- package/dist/output/index.d.ts +1 -0
- package/dist/output/index.d.ts.map +1 -1
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/scripts/sync-linear-templates.d.ts +26 -0
- package/dist/scripts/sync-linear-templates.d.ts.map +1 -0
- package/dist/scripts/sync-linear-templates.js +115 -0
- package/dist/scripts/sync-linear-templates.js.map +1 -0
- package/dist/signals/github-commits.d.ts +31 -0
- package/dist/signals/github-commits.d.ts.map +1 -0
- package/dist/signals/github-commits.js +127 -0
- package/dist/signals/github-commits.js.map +1 -0
- package/dist/signals/github-pr.d.ts +16 -0
- package/dist/signals/github-pr.d.ts.map +1 -0
- package/dist/signals/github-pr.js +98 -0
- package/dist/signals/github-pr.js.map +1 -0
- package/dist/signals/index.d.ts +4 -0
- package/dist/signals/index.d.ts.map +1 -1
- package/dist/signals/index.js +4 -0
- package/dist/signals/index.js.map +1 -1
- package/dist/signals/linear-issues.d.ts +20 -0
- package/dist/signals/linear-issues.d.ts.map +1 -0
- package/dist/signals/linear-issues.js +115 -0
- package/dist/signals/linear-issues.js.map +1 -0
- package/dist/signals/registry.d.ts +4 -3
- package/dist/signals/registry.d.ts.map +1 -1
- package/dist/signals/registry.js +33 -11
- package/dist/signals/registry.js.map +1 -1
- package/dist/signals/slack-messages.d.ts +20 -0
- package/dist/signals/slack-messages.d.ts.map +1 -0
- package/dist/signals/slack-messages.js +129 -0
- package/dist/signals/slack-messages.js.map +1 -0
- package/dist/utils/errors.d.ts +81 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +110 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/label-policy.d.ts +60 -0
- package/dist/utils/label-policy.d.ts.map +1 -0
- package/dist/utils/label-policy.js +103 -0
- package/dist/utils/label-policy.js.map +1 -0
- package/dist/utils/parse.d.ts +48 -0
- package/dist/utils/parse.d.ts.map +1 -0
- package/dist/utils/parse.js +133 -0
- package/dist/utils/parse.js.map +1 -0
- package/dist/utils/project-status.d.ts +6 -0
- package/dist/utils/project-status.d.ts.map +1 -0
- package/dist/utils/project-status.js +33 -0
- package/dist/utils/project-status.js.map +1 -0
- package/dist/utils/rate-limit.d.ts +24 -0
- package/dist/utils/rate-limit.d.ts.map +1 -0
- package/dist/utils/rate-limit.js +89 -0
- package/dist/utils/rate-limit.js.map +1 -0
- package/dist/utils/resolve.d.ts +84 -0
- package/dist/utils/resolve.d.ts.map +1 -0
- package/dist/utils/resolve.js +172 -0
- package/dist/utils/resolve.js.map +1 -0
- package/dist/utils/sleep.d.ts +2 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +4 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/webhook-verify.d.ts +42 -0
- package/dist/utils/webhook-verify.d.ts.map +1 -0
- package/dist/utils/webhook-verify.js +65 -0
- package/dist/utils/webhook-verify.js.map +1 -0
- package/package.json +7 -2
- package/references/agent-description-template.md +31 -0
- package/references/cli-reference.md +227 -0
- package/references/curator-tiering-rules.md +78 -0
- package/references/label-policy.example.json +37 -0
- package/references/label-policy.placeholder.json +6 -0
- package/references/settings-template.md +30 -0
- package/references/signal-sources.example.json +0 -8
- package/references/sla-reference.md +70 -0
- package/references/template-index.md +34 -0
- package/references/workspace-labels.md +124 -0
- package/references/workspace-projects.md +56 -0
- package/references/workspace-routing.md +58 -0
- package/schemas/label-policy.json +72 -0
- package/scripts/postinstall.mjs +195 -0
- package/skills/linear-workspace/SKILL.md +65 -4
- package/templates/ACC-PRO-provision.md +74 -0
- package/templates/ACC-PRV-privileged.md +66 -0
- package/templates/ACC-QTR-review.md +77 -0
- package/templates/ACC-REV-revoke.md +67 -0
- package/templates/AI-USE-capability.md +111 -0
- package/templates/AUD-CAP-corrective.md +89 -0
- package/templates/AUD-INT-internal.md +92 -0
- package/templates/AUD-MGT-management.md +110 -0
- package/templates/CHG-MAJ-major.md +110 -0
- package/templates/CHG-SIG-significant.md +83 -0
- package/templates/CHG-STD-standard.md +47 -0
- package/templates/LRN-DOC-lessons.md +75 -0
- package/templates/OPS-BCK-backup.md +99 -0
- package/templates/OPS-DAT-data-mod.md +98 -0
- package/templates/RCA-DOC-root-cause.md +105 -0
- package/templates/RSK-ASS-assessment.md +87 -0
- package/templates/RSK-VND-vendor.md +113 -0
- package/templates/SEC-INC-incident.md +76 -0
- package/templates/SEC-PEN-pentest.md +58 -0
- package/templates/SEC-VLN-vulnerability.md +69 -0
- package/templates/SLA-AVL-availability.md +86 -0
- package/templates/SLA-OPS-operational.md +70 -0
- package/templates/agent-server-template/README.md +88 -0
- package/templates/agent-server-template/server.example.ts +185 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# SEC-INC: Security Incident Response
|
|
2
|
+
|
|
3
|
+
## Quick Reference
|
|
4
|
+
- **SLA:** 15min-72hrs (severity-based)
|
|
5
|
+
- **Team:** *the team that owns this workflow in your workspace*
|
|
6
|
+
- **Project:** Security Incidents
|
|
7
|
+
|
|
8
|
+
## Required Labels
|
|
9
|
+
- `Type: bug`
|
|
10
|
+
- `Layer: devops` or `Layer: backend`
|
|
11
|
+
- `Flag: security`
|
|
12
|
+
- `Flag: compliance`
|
|
13
|
+
- Severity label (see classification)
|
|
14
|
+
|
|
15
|
+
## Severity Classification
|
|
16
|
+
| Severity | Response Time | Post-Review | Examples |
|
|
17
|
+
|----------|---------------|-------------|----------|
|
|
18
|
+
| Sev 0 (Critical) | 15 minutes | 3 days | Data breach, full outage, active attack |
|
|
19
|
+
| Sev 1 (High) | 1 hour | 5 days | Partial outage, unauthorized access attempt |
|
|
20
|
+
| Sev 2 (Medium) | 4 hours | 10 days | Failed control, suspicious activity |
|
|
21
|
+
| Sev 3 (Low) | 24 hours | 20 days | Policy violation, near-miss |
|
|
22
|
+
|
|
23
|
+
## Issue Template
|
|
24
|
+
```markdown
|
|
25
|
+
## Security Incident Report
|
|
26
|
+
|
|
27
|
+
**Incident ID:** SEC-YYYY-XXX
|
|
28
|
+
**Date/Time Discovered:** [YYYY-MM-DD HH:MM UTC]
|
|
29
|
+
**Date/Time Reported:** [YYYY-MM-DD HH:MM UTC]
|
|
30
|
+
**Severity:** [Sev 0 / Sev 1 / Sev 2 / Sev 3]
|
|
31
|
+
|
|
32
|
+
## Classification
|
|
33
|
+
- **Incident Type:** [Denial of Service / Unauthorized Access / Malicious Code / Data Breach / Policy Violation / Other]
|
|
34
|
+
- **Affected Systems:** [List systems/services affected]
|
|
35
|
+
- **Data Involved:** [Yes/No - if Yes, describe data types]
|
|
36
|
+
- **Customer Impact:** [Yes/No - if Yes, describe impact]
|
|
37
|
+
|
|
38
|
+
## Initial Assessment
|
|
39
|
+
[Brief description of what happened and initial impact assessment]
|
|
40
|
+
|
|
41
|
+
## Detection Method
|
|
42
|
+
- [ ] Automated monitoring/alerting
|
|
43
|
+
- [ ] User report
|
|
44
|
+
- [ ] Security scan
|
|
45
|
+
- [ ] Third-party notification
|
|
46
|
+
- [ ] Other: ___
|
|
47
|
+
|
|
48
|
+
## Containment Actions Taken
|
|
49
|
+
- [ ] Isolated affected systems
|
|
50
|
+
- [ ] Preserved evidence (logs, screenshots)
|
|
51
|
+
- [ ] Reset compromised credentials
|
|
52
|
+
- [ ] Other: ___
|
|
53
|
+
|
|
54
|
+
## Timeline of Events
|
|
55
|
+
| Time | Event |
|
|
56
|
+
|------|-------|
|
|
57
|
+
| | |
|
|
58
|
+
|
|
59
|
+
## Escalation
|
|
60
|
+
- [ ] CTO notified (required for Sev 0/1)
|
|
61
|
+
- [ ] Legal notified (if data involved)
|
|
62
|
+
- [ ] Customer notification required? [Yes/No]
|
|
63
|
+
|
|
64
|
+
## Next Steps
|
|
65
|
+
1. [Action item]
|
|
66
|
+
2. [Action item]
|
|
67
|
+
|
|
68
|
+
## Resources
|
|
69
|
+
- Incident Response Process: `/security-compliance/incident-management/incident-response-process.md`
|
|
70
|
+
- RCA Template: Create linked RCA-DOC issue if Sev 0/1/2
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Escalation Rules
|
|
74
|
+
- Sev 0/1: Immediate notification to CTO
|
|
75
|
+
- Customer data involved: Notify Legal/CEO
|
|
76
|
+
- Breach determination: CEO + Legal review required
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# SEC-PEN: Penetration Test Remediation
|
|
2
|
+
|
|
3
|
+
## Quick Reference
|
|
4
|
+
- **SLA:** 30-90 days (severity-based)
|
|
5
|
+
- **Team:** *the team that owns this workflow in your workspace*
|
|
6
|
+
- **Project:** Pentest Remediation
|
|
7
|
+
|
|
8
|
+
## Required Labels
|
|
9
|
+
- `Type: bug`
|
|
10
|
+
- `Flag: security`
|
|
11
|
+
- `Source: Penetration Test`
|
|
12
|
+
- Severity label based on finding risk rating
|
|
13
|
+
|
|
14
|
+
## Issue Template
|
|
15
|
+
```markdown
|
|
16
|
+
## Penetration Test Finding Remediation
|
|
17
|
+
|
|
18
|
+
**Finding ID:** [From pentest report, e.g., PT-2025-001]
|
|
19
|
+
**Test Date:** [YYYY-MM-DD]
|
|
20
|
+
**Testing Firm:** [e.g., Workstreet]
|
|
21
|
+
**Report Section:** [Reference to report section]
|
|
22
|
+
|
|
23
|
+
## Finding Details
|
|
24
|
+
- **Title:** [Finding title from report]
|
|
25
|
+
- **Severity:** [Critical / High / Medium / Low]
|
|
26
|
+
- **CVSS Score:** [X.X] (if provided)
|
|
27
|
+
- **Category:** [OWASP Top 10 category if applicable]
|
|
28
|
+
|
|
29
|
+
## Description
|
|
30
|
+
[Copy finding description from pentest report]
|
|
31
|
+
|
|
32
|
+
## Affected Component
|
|
33
|
+
- **URL/Endpoint:** [Affected URL or API endpoint]
|
|
34
|
+
- **System:** [Frontend / Backend / Infrastructure]
|
|
35
|
+
|
|
36
|
+
## Proof of Concept
|
|
37
|
+
[Summary of how tester exploited the vulnerability]
|
|
38
|
+
|
|
39
|
+
## Recommended Remediation
|
|
40
|
+
[Copy recommendation from pentest report]
|
|
41
|
+
|
|
42
|
+
## Our Remediation Plan
|
|
43
|
+
[Describe specific steps we will take]
|
|
44
|
+
|
|
45
|
+
## Acceptance Criteria
|
|
46
|
+
- [ ] Vulnerability remediated
|
|
47
|
+
- [ ] Verified by internal testing
|
|
48
|
+
- [ ] Ready for re-test by penetration tester
|
|
49
|
+
|
|
50
|
+
## Verification
|
|
51
|
+
- [ ] Internal verification complete
|
|
52
|
+
- [ ] Re-test requested from [Testing Firm]
|
|
53
|
+
- [ ] Re-test passed
|
|
54
|
+
|
|
55
|
+
## References
|
|
56
|
+
- Full Report: [Link to pentest report in Notion]
|
|
57
|
+
- Related CVEs: [If applicable]
|
|
58
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# SEC-VLN: Vulnerability Remediation
|
|
2
|
+
|
|
3
|
+
## Quick Reference
|
|
4
|
+
- **SLA:** 30-90 days (severity-based)
|
|
5
|
+
- **Team:** *the team that owns this workflow in your workspace*
|
|
6
|
+
- **Project:** Vulnerability Management
|
|
7
|
+
|
|
8
|
+
## Required Labels
|
|
9
|
+
- `Type: bug`
|
|
10
|
+
- `Flag: security`
|
|
11
|
+
- `Source: Vulnerability Scan` OR `Source: CodeQL` OR `Source: Penetration Test`
|
|
12
|
+
- Severity label based on CVSS score
|
|
13
|
+
|
|
14
|
+
## Severity to SLA Mapping
|
|
15
|
+
| Severity | CVSS Score | Remediation SLA |
|
|
16
|
+
|----------|------------|-----------------|
|
|
17
|
+
| Critical | 9.0-10.0 | 30 days |
|
|
18
|
+
| High | 7.0-8.9 | 30 days |
|
|
19
|
+
| Medium | 4.0-6.9 | 60 days |
|
|
20
|
+
| Low | 0.1-3.9 | 90 days |
|
|
21
|
+
|
|
22
|
+
## Issue Template
|
|
23
|
+
```markdown
|
|
24
|
+
## Vulnerability Remediation
|
|
25
|
+
|
|
26
|
+
**Vulnerability ID:** [CVE-XXXX-XXXXX or internal ID]
|
|
27
|
+
**First Detected:** [YYYY-MM-DD]
|
|
28
|
+
**Detection Source:** [Dependabot / CodeQL / AWS Inspector / Penetration Test / Manual]
|
|
29
|
+
**Remediation Deadline:** [YYYY-MM-DD] (per SLA)
|
|
30
|
+
|
|
31
|
+
## Vulnerability Details
|
|
32
|
+
- **Affected Component:** [Package/library/system name]
|
|
33
|
+
- **Current Version:** [x.x.x]
|
|
34
|
+
- **Fixed Version:** [x.x.x] (if known)
|
|
35
|
+
- **CVSS Score:** [X.X]
|
|
36
|
+
- **Severity:** [Critical / High / Medium / Low]
|
|
37
|
+
|
|
38
|
+
## Description
|
|
39
|
+
[Brief description of the vulnerability and potential impact]
|
|
40
|
+
|
|
41
|
+
## Affected Systems
|
|
42
|
+
- [ ] Frontend
|
|
43
|
+
- [ ] Backend (.NET)
|
|
44
|
+
- [ ] AI Server (Python)
|
|
45
|
+
- [ ] Infrastructure (AWS)
|
|
46
|
+
|
|
47
|
+
## Business Impact Assessment
|
|
48
|
+
[Describe potential business impact if exploited]
|
|
49
|
+
|
|
50
|
+
## Proposed Remediation
|
|
51
|
+
- [ ] Upgrade dependency to version [x.x.x]
|
|
52
|
+
- [ ] Apply security patch
|
|
53
|
+
- [ ] Configuration change
|
|
54
|
+
- [ ] Code fix
|
|
55
|
+
- [ ] Accept risk (requires documented justification)
|
|
56
|
+
|
|
57
|
+
## Risk Treatment Plan (if SLA cannot be met)
|
|
58
|
+
[Document justification and extended timeline if applicable]
|
|
59
|
+
|
|
60
|
+
## Verification
|
|
61
|
+
- [ ] Fix implemented
|
|
62
|
+
- [ ] Re-scanned to confirm resolution
|
|
63
|
+
- [ ] No regression in functionality
|
|
64
|
+
|
|
65
|
+
## References
|
|
66
|
+
- CVE Link: [URL]
|
|
67
|
+
- Advisory: [URL]
|
|
68
|
+
- Fix PR: [URL]
|
|
69
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# SLA-AVL: Platform Availability Incident
|
|
2
|
+
|
|
3
|
+
## Quick Reference
|
|
4
|
+
- **SLA:** 1hr-14 days (severity-based)
|
|
5
|
+
- **Team:** *the team that owns this workflow in your workspace*
|
|
6
|
+
- **Project:** Availability Incidents
|
|
7
|
+
|
|
8
|
+
## Severity Classification
|
|
9
|
+
| Severity | Description | Response SLA | Resolution Target |
|
|
10
|
+
|----------|-------------|--------------|-------------------|
|
|
11
|
+
| Critical | Complete outage | 1 hour | 8 hours |
|
|
12
|
+
| Medium | Core function impaired | 4 hours | 3 business days |
|
|
13
|
+
| Low | Minor issues | 1 business day | 14 business days |
|
|
14
|
+
|
|
15
|
+
## Required Labels
|
|
16
|
+
- `Type: bug`
|
|
17
|
+
- `Layer: [affected layer]`
|
|
18
|
+
- Severity label based on impact
|
|
19
|
+
|
|
20
|
+
## Issue Template
|
|
21
|
+
```markdown
|
|
22
|
+
## Platform Availability Incident
|
|
23
|
+
|
|
24
|
+
**Incident ID:** SLA-AVL-YYYY-XXX
|
|
25
|
+
**Start Time:** [YYYY-MM-DD HH:MM UTC]
|
|
26
|
+
**Detection Time:** [YYYY-MM-DD HH:MM UTC]
|
|
27
|
+
**Severity:** [Critical / Medium / Low]
|
|
28
|
+
|
|
29
|
+
## Impact Assessment
|
|
30
|
+
- **Services Affected:** [List affected services]
|
|
31
|
+
- **Users Affected:** [All / Partial - describe scope]
|
|
32
|
+
- **Customer Impact:** [Description of customer-facing impact]
|
|
33
|
+
- **Workaround Available:** [Yes - describe / No]
|
|
34
|
+
|
|
35
|
+
## Incident Description
|
|
36
|
+
[Brief description of the outage/issue]
|
|
37
|
+
|
|
38
|
+
## Timeline
|
|
39
|
+
| Time (UTC) | Event |
|
|
40
|
+
|------------|-------|
|
|
41
|
+
| | Issue started |
|
|
42
|
+
| | Issue detected |
|
|
43
|
+
| | Investigation started |
|
|
44
|
+
| | |
|
|
45
|
+
|
|
46
|
+
## Investigation
|
|
47
|
+
|
|
48
|
+
### Initial Assessment
|
|
49
|
+
[What was observed, initial hypothesis]
|
|
50
|
+
|
|
51
|
+
### Root Cause
|
|
52
|
+
[If identified - otherwise "Under investigation"]
|
|
53
|
+
|
|
54
|
+
### Affected Components
|
|
55
|
+
- [ ] Frontend
|
|
56
|
+
- [ ] Backend API
|
|
57
|
+
- [ ] Database
|
|
58
|
+
- [ ] AI Server
|
|
59
|
+
- [ ] AWS Infrastructure
|
|
60
|
+
- [ ] Third-party service: [Name]
|
|
61
|
+
|
|
62
|
+
## Resolution
|
|
63
|
+
|
|
64
|
+
### Actions Taken
|
|
65
|
+
1. [Action 1]
|
|
66
|
+
2. [Action 2]
|
|
67
|
+
|
|
68
|
+
### Resolution Time
|
|
69
|
+
- **Incident End Time:** [YYYY-MM-DD HH:MM UTC]
|
|
70
|
+
- **Total Duration:** [X hours Y minutes]
|
|
71
|
+
- **Resolution SLA Met:** [Yes / No]
|
|
72
|
+
|
|
73
|
+
## Customer Communication
|
|
74
|
+
- [ ] Status page updated
|
|
75
|
+
- [ ] Affected customers notified
|
|
76
|
+
- [ ] Resolution notification sent
|
|
77
|
+
|
|
78
|
+
## Follow-up Required
|
|
79
|
+
- [ ] Root Cause Analysis (create linked RCA-DOC if Critical/Medium)
|
|
80
|
+
- [ ] Preventive measures identified
|
|
81
|
+
- [ ] Post-mortem scheduled
|
|
82
|
+
|
|
83
|
+
## SLA Credit Assessment
|
|
84
|
+
- **Uptime this month:** [XX.XX%]
|
|
85
|
+
- **Credit applicable:** [Yes - X% / No]
|
|
86
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SLA-OPS: Operational Request
|
|
2
|
+
|
|
3
|
+
## Quick Reference
|
|
4
|
+
- **SLA:** 3-14 days
|
|
5
|
+
- **Team:** *the team that owns this workflow in your workspace*
|
|
6
|
+
- **Project:** Operational Requests
|
|
7
|
+
|
|
8
|
+
## Priority to SLA Mapping
|
|
9
|
+
| Priority | Resolution Timeframe |
|
|
10
|
+
|----------|---------------------|
|
|
11
|
+
| High | 3 days |
|
|
12
|
+
| Medium | 7 days |
|
|
13
|
+
| Low | 14 days |
|
|
14
|
+
|
|
15
|
+
## Required Labels
|
|
16
|
+
- `Type: feature`
|
|
17
|
+
- `Layer: devops`
|
|
18
|
+
- `Infrastructure` (if infrastructure work)
|
|
19
|
+
- `Account Setup` (if account work)
|
|
20
|
+
|
|
21
|
+
## Issue Template
|
|
22
|
+
```markdown
|
|
23
|
+
## Operational Request
|
|
24
|
+
|
|
25
|
+
**Request ID:** SLA-OPS-YYYY-XXX
|
|
26
|
+
**Request Date:** [YYYY-MM-DD]
|
|
27
|
+
**Requestor:** [Name]
|
|
28
|
+
**Priority:** [High / Medium / Low]
|
|
29
|
+
**SLA Deadline:** [YYYY-MM-DD]
|
|
30
|
+
|
|
31
|
+
## Request Type
|
|
32
|
+
- [ ] Infrastructure provisioning
|
|
33
|
+
- [ ] Account setup
|
|
34
|
+
- [ ] Configuration change
|
|
35
|
+
- [ ] Access modification
|
|
36
|
+
- [ ] Other: ___
|
|
37
|
+
|
|
38
|
+
## Request Details
|
|
39
|
+
[Detailed description of what is being requested]
|
|
40
|
+
|
|
41
|
+
## Business Justification
|
|
42
|
+
[Why is this request needed?]
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
[Specific technical requirements]
|
|
46
|
+
|
|
47
|
+
## Dependencies
|
|
48
|
+
- [ ] [Dependency 1]
|
|
49
|
+
- [ ] [Dependency 2]
|
|
50
|
+
|
|
51
|
+
## Implementation Plan
|
|
52
|
+
1. [Step 1]
|
|
53
|
+
2. [Step 2]
|
|
54
|
+
3. [Step 3]
|
|
55
|
+
|
|
56
|
+
## Verification Criteria
|
|
57
|
+
- [ ] [How to verify completion]
|
|
58
|
+
|
|
59
|
+
## Approvals
|
|
60
|
+
- [ ] Manager approval: _________________ Date: _______
|
|
61
|
+
- [ ] Technical approval (if needed): _________________ Date: _______
|
|
62
|
+
|
|
63
|
+
## Completion
|
|
64
|
+
- [ ] Request fulfilled
|
|
65
|
+
- [ ] Requestor notified
|
|
66
|
+
- [ ] Documentation updated
|
|
67
|
+
|
|
68
|
+
**Completion Date:** [YYYY-MM-DD]
|
|
69
|
+
**SLA Met:** [Yes / No]
|
|
70
|
+
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Agent webhook receiver — reference template
|
|
2
|
+
|
|
3
|
+
This directory is **a template, not a running server**. The Linear plugin is a CLI; it doesn't host long-running HTTP listeners. If you want the plugin to act as a Linear agent (the `actor=app` model where users `@mention` it or assign issues to it), you need to host this receiver somewhere — copy `server.example.ts` into your own project and adapt.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
1. Listens for `POST /linear/webhook` from Linear.
|
|
8
|
+
2. Verifies the `linear-signature` HMAC-SHA256 header against the body using your stored signing secret.
|
|
9
|
+
3. On `agentSessionEvent.created`, emits a `thought` activity within 10 seconds via the plugin's CLI to ack the session.
|
|
10
|
+
4. Hands off to your actual agent logic (which you write).
|
|
11
|
+
|
|
12
|
+
## Where to host
|
|
13
|
+
|
|
14
|
+
| Host | Notes |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Cloudflare Workers | Linear's own `linear/linear-agent-demo` uses Workers. Edit the example to use the Fetch API runtime. |
|
|
17
|
+
| Vercel / Netlify functions | Fine for low traffic. Same Fetch API edits. |
|
|
18
|
+
| Fly.io / Railway / Render | Run the example mostly as-is on Node. |
|
|
19
|
+
| Local + ngrok | For dev only — Linear retries 3× on failure (1 min / 1 hr / 6 hr). |
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 1. Build the plugin CLI so the template can call it.
|
|
25
|
+
npm install -g @elnora-ai/linear
|
|
26
|
+
|
|
27
|
+
# 2. Copy this template into your own project.
|
|
28
|
+
cp -r templates/agent-server-template /path/to/your/agent-server
|
|
29
|
+
|
|
30
|
+
# 3. Register the webhook with Linear.
|
|
31
|
+
linear webhooks create \
|
|
32
|
+
--url https://your-public-url/linear/webhook \
|
|
33
|
+
--resource-types AgentSessionEvent \
|
|
34
|
+
--all-public-teams
|
|
35
|
+
# Save the secret it returns into LINEAR_WEBHOOK_SECRET.
|
|
36
|
+
|
|
37
|
+
# 4. Run the receiver.
|
|
38
|
+
LINEAR_API_KEY=lin_api_... \
|
|
39
|
+
LINEAR_WEBHOOK_SECRET=lin_wh_... \
|
|
40
|
+
node server.example.js
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What you fill in
|
|
44
|
+
|
|
45
|
+
The template stops after acking the session. The real agent logic — running Claude Code, opening a PR, posting back to Linear — goes in the `// TODO` block. Common patterns:
|
|
46
|
+
|
|
47
|
+
- **Quick echo bot:** emit a `response` activity with whatever you want to say.
|
|
48
|
+
- **Coding agent:** `child_process.spawn` your dev workflow (e.g. `claude` + a prompt that includes the issue context).
|
|
49
|
+
- **Triage routing:** read the issue, decide if it's for the bot, otherwise emit a `response` and exit.
|
|
50
|
+
|
|
51
|
+
Use the CLI for everything Linear-side:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
linear agent-activities create <sessionId> --type thought --body "Working on it"
|
|
55
|
+
linear agent-activities create <sessionId> --type response --body "Done. PR: <url>"
|
|
56
|
+
linear agent-sessions update-external-url <sessionId> --add https://github.com/.../pull/42
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Security notes
|
|
60
|
+
|
|
61
|
+
- **Always** verify the signature before reading the body. The template ships an inline `verifyLinearWebhook` helper (HMAC-SHA256 + `timingSafeEqual`) — don't bypass it.
|
|
62
|
+
- **Never** commit `LINEAR_WEBHOOK_SECRET` or `LINEAR_API_KEY`. Use your host's secret store.
|
|
63
|
+
- **Rotate** the webhook secret with `linear webhooks rotate-secret <id> --yes` if you suspect a leak. The new secret is shown ONCE.
|
|
64
|
+
- **Respond fast.** Linear retries on non-200 or >5s. ACK within 200ms by responding `200 ok` immediately, then doing the work async.
|
|
65
|
+
|
|
66
|
+
## Activity types & lifecycle
|
|
67
|
+
|
|
68
|
+
| Type | Body | When to use |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `thought` | text | Internal narration, including the mandatory 10s ack |
|
|
71
|
+
| `action` | `{action, parameter, result}` | Tool call summary (e.g. "ran `gh pr create`") |
|
|
72
|
+
| `elicitation` | text + `signal` (`select` / `auth`) | Ask the user (multi-choice or auth URL) |
|
|
73
|
+
| `response` | text | Final visible answer |
|
|
74
|
+
| `error` | text | Visible failure message |
|
|
75
|
+
|
|
76
|
+
The session is considered "stale" after 30 minutes of inactivity but is recoverable — emit any activity to revive.
|
|
77
|
+
|
|
78
|
+
## Troubleshooting
|
|
79
|
+
|
|
80
|
+
- **401 in your logs**: signature mismatch. Check `LINEAR_WEBHOOK_SECRET` matches what `linear webhooks list` shows.
|
|
81
|
+
- **No webhook delivered**: confirm `agentSessionEvent` is in the webhook's `resourceTypes`.
|
|
82
|
+
- **"Agent unresponsive" in Linear UI**: you took longer than 10s to emit a thought. Move the ack BEFORE any expensive work.
|
|
83
|
+
|
|
84
|
+
## See also
|
|
85
|
+
|
|
86
|
+
- Plugin CLI: `linear webhooks --help`, `linear agent-sessions --help`, `linear agent-activities --help`
|
|
87
|
+
- Linear's official agent docs: https://linear.app/developers/agents
|
|
88
|
+
- Linear's reference Cloudflare-Worker demo: https://github.com/linear/linear-agent-demo
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference webhook receiver for Linear's agent framework.
|
|
3
|
+
*
|
|
4
|
+
* This is a TEMPLATE — it ships with the plugin so operators can run their
|
|
5
|
+
* own agent receiver wherever they want (Cloudflare Worker, Vercel function,
|
|
6
|
+
* Fly machine, etc.). The plugin itself is a CLI; it does not host this
|
|
7
|
+
* server for you.
|
|
8
|
+
*
|
|
9
|
+
* 1. Register a webhook in Linear pointing at this server's URL:
|
|
10
|
+
* linear webhooks create \
|
|
11
|
+
* --url https://your-host/linear/webhook \
|
|
12
|
+
* --resource-types AgentSessionEvent \
|
|
13
|
+
* --all-public-teams
|
|
14
|
+
* (Linear will return a signing secret — store as LINEAR_WEBHOOK_SECRET.)
|
|
15
|
+
*
|
|
16
|
+
* 2. Run this server with both env vars set:
|
|
17
|
+
* LINEAR_API_KEY=lin_api_... \
|
|
18
|
+
* LINEAR_WEBHOOK_SECRET=lin_wh_... \
|
|
19
|
+
* node server.example.js
|
|
20
|
+
*
|
|
21
|
+
* 3. Linear delivers `agentSessionEvent` POSTs to /linear/webhook. We
|
|
22
|
+
* verify the signature, then must emit a `thought` activity within
|
|
23
|
+
* 10 seconds to acknowledge.
|
|
24
|
+
*
|
|
25
|
+
* The activity-create call shells out to the plugin's CLI so you don't end
|
|
26
|
+
* up with two implementations. The signature verifier is inlined below so
|
|
27
|
+
* this file works after `cp -r` out of the plugin tree — no relative
|
|
28
|
+
* imports back into the plugin source.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import http from "node:http";
|
|
32
|
+
import { execFile } from "node:child_process";
|
|
33
|
+
import { promisify } from "node:util";
|
|
34
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
35
|
+
|
|
36
|
+
const execFileAsync = promisify(execFile);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Verify a Linear webhook payload's HMAC-SHA256 signature AND its replay
|
|
40
|
+
* window. Mirrors the implementation in the plugin's webhook-verify util —
|
|
41
|
+
* kept inline so this template stands alone after copy.
|
|
42
|
+
*
|
|
43
|
+
* Replay protection: when `timestamp` (the `webhookTimestamp` field from the
|
|
44
|
+
* parsed body, Unix ms) is provided, payloads older than `maxAgeMs` are
|
|
45
|
+
* rejected. Without this, an attacker who captures a valid signed request
|
|
46
|
+
* can replay it forever.
|
|
47
|
+
*/
|
|
48
|
+
// Linear recommends 60 s — see https://linear.app/developers/webhooks
|
|
49
|
+
const DEFAULT_WEBHOOK_MAX_AGE_MS = 60 * 1000;
|
|
50
|
+
|
|
51
|
+
function verifyLinearWebhook(opts: {
|
|
52
|
+
rawBody: string | Buffer;
|
|
53
|
+
signature: string;
|
|
54
|
+
secret: string;
|
|
55
|
+
timestamp?: number;
|
|
56
|
+
maxAgeMs?: number;
|
|
57
|
+
}): boolean {
|
|
58
|
+
if (!opts.signature || !opts.secret) return false;
|
|
59
|
+
const body = typeof opts.rawBody === "string" ? Buffer.from(opts.rawBody, "utf-8") : opts.rawBody;
|
|
60
|
+
const expectedHex = createHmac("sha256", opts.secret).update(body).digest("hex");
|
|
61
|
+
const expected = Buffer.from(expectedHex, "hex");
|
|
62
|
+
let received: Buffer;
|
|
63
|
+
try {
|
|
64
|
+
received = Buffer.from(opts.signature, "hex");
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (received.length !== expected.length) return false;
|
|
69
|
+
if (!timingSafeEqual(received, expected)) return false;
|
|
70
|
+
|
|
71
|
+
if (typeof opts.timestamp === "number") {
|
|
72
|
+
if (!Number.isFinite(opts.timestamp)) return false;
|
|
73
|
+
const maxAgeMs = opts.maxAgeMs ?? DEFAULT_WEBHOOK_MAX_AGE_MS;
|
|
74
|
+
if (Math.abs(Date.now() - opts.timestamp) > maxAgeMs) return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const PORT = Number(process.env.PORT ?? 8787);
|
|
80
|
+
const SECRET = process.env.LINEAR_WEBHOOK_SECRET;
|
|
81
|
+
const CLI_PATH = process.env.LINEAR_CLI_PATH ?? "../../cli/bin/linear.js";
|
|
82
|
+
|
|
83
|
+
if (!SECRET) {
|
|
84
|
+
console.error("LINEAR_WEBHOOK_SECRET not set — refusing to start.");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
if (!process.env.LINEAR_API_KEY) {
|
|
88
|
+
console.error("LINEAR_API_KEY not set — refusing to start.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface AgentSessionEvent {
|
|
93
|
+
type: "AgentSessionEvent";
|
|
94
|
+
action: "created" | "prompted";
|
|
95
|
+
agentSession?: { id: string; type?: string };
|
|
96
|
+
promptContext?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function readBody(req: http.IncomingMessage): Promise<Buffer> {
|
|
100
|
+
const chunks: Buffer[] = [];
|
|
101
|
+
for await (const chunk of req) {
|
|
102
|
+
chunks.push(chunk as Buffer);
|
|
103
|
+
}
|
|
104
|
+
return Buffer.concat(chunks);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Emit a thought activity via the CLI (one process per call — fine for ack). */
|
|
108
|
+
async function emitThought(sessionId: string, body: string): Promise<void> {
|
|
109
|
+
await execFileAsync("node", [
|
|
110
|
+
CLI_PATH,
|
|
111
|
+
"agent-activities", "create", sessionId,
|
|
112
|
+
"--type", "thought",
|
|
113
|
+
"--body", body,
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const server = http.createServer(async (req, res) => {
|
|
118
|
+
if (req.method !== "POST" || req.url !== "/linear/webhook") {
|
|
119
|
+
res.writeHead(404);
|
|
120
|
+
res.end();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const signature = req.headers["linear-signature"];
|
|
124
|
+
if (typeof signature !== "string") {
|
|
125
|
+
res.writeHead(400);
|
|
126
|
+
res.end("Missing linear-signature header");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const rawBody = await readBody(req);
|
|
130
|
+
// HMAC-verify on the raw bytes BEFORE touching JSON.parse — never let
|
|
131
|
+
// unverified input reach the parser. Replay-window check happens after,
|
|
132
|
+
// once we trust the body enough to read webhookTimestamp out of it.
|
|
133
|
+
if (!verifyLinearWebhook({ rawBody, signature, secret: SECRET })) {
|
|
134
|
+
res.writeHead(401);
|
|
135
|
+
res.end("Invalid signature");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
let parsedForTimestamp: { webhookTimestamp?: number } = {};
|
|
139
|
+
try {
|
|
140
|
+
parsedForTimestamp = JSON.parse(rawBody.toString("utf-8"));
|
|
141
|
+
} catch {
|
|
142
|
+
res.writeHead(400);
|
|
143
|
+
res.end("Body is not valid JSON");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const timestamp = parsedForTimestamp.webhookTimestamp;
|
|
147
|
+
if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
|
|
148
|
+
res.writeHead(401);
|
|
149
|
+
res.end("Missing or invalid webhookTimestamp");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (Math.abs(Date.now() - timestamp) > DEFAULT_WEBHOOK_MAX_AGE_MS) {
|
|
153
|
+
res.writeHead(401);
|
|
154
|
+
res.end("Expired payload");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// ACK Linear within 5s — non-200 triggers a retry and counts against agent
|
|
158
|
+
// responsiveness. Do work async after responding.
|
|
159
|
+
res.writeHead(200);
|
|
160
|
+
res.end("ok");
|
|
161
|
+
|
|
162
|
+
const payload = JSON.parse(rawBody.toString("utf-8")) as AgentSessionEvent;
|
|
163
|
+
if (payload.type !== "AgentSessionEvent") return;
|
|
164
|
+
if (payload.action !== "created") return;
|
|
165
|
+
const sessionId = payload.agentSession?.id;
|
|
166
|
+
if (!sessionId) return;
|
|
167
|
+
|
|
168
|
+
// Linear requires a thought within 10s — emit immediately then start work.
|
|
169
|
+
try {
|
|
170
|
+
await emitThought(sessionId, "Got it — picking this up now.");
|
|
171
|
+
} catch (e) {
|
|
172
|
+
console.error("Failed to emit ack thought:", e);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// TODO: kick off whatever the agent actually does. Examples:
|
|
177
|
+
// - Run /linear-work as a subprocess (background coding agent)
|
|
178
|
+
// - Trigger your own task queue
|
|
179
|
+
// - Update the issue with progress via `linear issues update`
|
|
180
|
+
console.log(`[agent] session ${sessionId} acknowledged`);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
server.listen(PORT, () => {
|
|
184
|
+
console.log(`Linear agent webhook receiver listening on :${PORT}/linear/webhook`);
|
|
185
|
+
});
|