@formthefog/stratus 2026.2.24 → 2026.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.github/sentinel/action.yml +100 -0
  2. package/.github/sentinel/dist/codebase.d.ts +3 -0
  3. package/.github/sentinel/dist/codebase.d.ts.map +1 -0
  4. package/.github/sentinel/dist/context.d.ts +6 -0
  5. package/.github/sentinel/dist/context.d.ts.map +1 -0
  6. package/.github/sentinel/dist/fixer.d.ts +6 -0
  7. package/.github/sentinel/dist/fixer.d.ts.map +1 -0
  8. package/.github/sentinel/dist/index.d.ts +1 -0
  9. package/.github/sentinel/dist/index.d.ts.map +1 -0
  10. package/.github/sentinel/dist/index.js +68808 -0
  11. package/.github/sentinel/dist/index.js.map +1 -0
  12. package/.github/sentinel/dist/licenses.txt +1152 -0
  13. package/.github/sentinel/dist/models/anthropic.d.ts +26 -0
  14. package/.github/sentinel/dist/models/anthropic.d.ts.map +1 -0
  15. package/.github/sentinel/dist/models/openai.d.ts +26 -0
  16. package/.github/sentinel/dist/models/openai.d.ts.map +1 -0
  17. package/.github/sentinel/dist/models/openrouter.d.ts +31 -0
  18. package/.github/sentinel/dist/models/openrouter.d.ts.map +1 -0
  19. package/.github/sentinel/dist/models/types.d.ts +37 -0
  20. package/.github/sentinel/dist/models/types.d.ts.map +1 -0
  21. package/.github/sentinel/dist/orchestrator.d.ts +3 -0
  22. package/.github/sentinel/dist/orchestrator.d.ts.map +1 -0
  23. package/.github/sentinel/dist/policy.d.ts +15 -0
  24. package/.github/sentinel/dist/policy.d.ts.map +1 -0
  25. package/.github/sentinel/dist/reporter.d.ts +8 -0
  26. package/.github/sentinel/dist/reporter.d.ts.map +1 -0
  27. package/.github/sentinel/dist/responder.d.ts +6 -0
  28. package/.github/sentinel/dist/responder.d.ts.map +1 -0
  29. package/.github/sentinel/dist/router.d.ts +2 -0
  30. package/.github/sentinel/dist/router.d.ts.map +1 -0
  31. package/.github/sentinel/dist/schemas/config.d.ts +195 -0
  32. package/.github/sentinel/dist/schemas/config.d.ts.map +1 -0
  33. package/.github/sentinel/dist/schemas/fix.d.ts +130 -0
  34. package/.github/sentinel/dist/schemas/fix.d.ts.map +1 -0
  35. package/.github/sentinel/dist/schemas/review.d.ts +275 -0
  36. package/.github/sentinel/dist/schemas/review.d.ts.map +1 -0
  37. package/.github/sentinel/dist/sourcemap-register.js +1 -0
  38. package/.github/sentinel/dist/subway.d.ts +31 -0
  39. package/.github/sentinel/dist/subway.d.ts.map +1 -0
  40. package/.github/sentinel/dist/types.d.ts +210 -0
  41. package/.github/sentinel/dist/types.d.ts.map +1 -0
  42. package/.github/sentinel/package-lock.json +2389 -0
  43. package/.github/sentinel/package.json +29 -0
  44. package/.github/sentinel/src/codebase.ts +265 -0
  45. package/.github/sentinel/src/context.ts +182 -0
  46. package/.github/sentinel/src/fixer.ts +353 -0
  47. package/.github/sentinel/src/index.ts +263 -0
  48. package/.github/sentinel/src/models/anthropic.ts +244 -0
  49. package/.github/sentinel/src/models/openai.ts +242 -0
  50. package/.github/sentinel/src/models/openrouter.ts +319 -0
  51. package/.github/sentinel/src/models/types.ts +35 -0
  52. package/.github/sentinel/src/orchestrator.ts +287 -0
  53. package/.github/sentinel/src/policy.ts +133 -0
  54. package/.github/sentinel/src/reporter.ts +666 -0
  55. package/.github/sentinel/src/responder.ts +156 -0
  56. package/.github/sentinel/src/router.ts +308 -0
  57. package/.github/sentinel/src/schemas/config.ts +84 -0
  58. package/.github/sentinel/src/schemas/fix.ts +44 -0
  59. package/.github/sentinel/src/schemas/review.ts +73 -0
  60. package/.github/sentinel/src/subway.ts +250 -0
  61. package/.github/sentinel/src/types.ts +234 -0
  62. package/.github/sentinel/tsconfig.json +19 -0
  63. package/.github/sentinel.yml +34 -0
  64. package/.github/workflows/sentinel.yml +55 -0
  65. package/README.md +88 -102
  66. package/SECURITY.md +21 -10
  67. package/TROUBLESHOOTING.md +2 -2
  68. package/index.ts +219 -109
  69. package/openclaw.plugin.json +50 -26
  70. package/package.json +1 -1
  71. package/skills/stratus-info/SKILL.md +70 -10
  72. package/src/client.ts +78 -18
  73. package/src/config.ts +29 -8
  74. package/src/setup.ts +53 -61
  75. package/src/types.ts +11 -0
@@ -0,0 +1,133 @@
1
+ import * as core from "@actions/core"
2
+ import * as github from "@actions/github"
3
+ import * as fs from "fs"
4
+ import { parse as parseYaml } from "yaml"
5
+ import { SentinelConfigSchema, type SentinelConfig } from "./schemas/config"
6
+ import type { RepoPolicies, ReviewMode, FindingSeverity, FixMode } from "./types"
7
+
8
+ type Octokit = ReturnType<typeof github.getOctokit>
9
+
10
+ export async function loadPolicies(
11
+ octokit: Octokit,
12
+ configPath: string,
13
+ modeOverride?: string
14
+ ): Promise<RepoPolicies> {
15
+ const raw = await loadConfigFile(octokit, configPath)
16
+ const config = parseConfig(raw)
17
+
18
+ const mode = (modeOverride as ReviewMode) || config.mode
19
+ const anthropicModel = core.getInput("anthropic_model") || config.models.anthropic.model
20
+ const openaiModel = core.getInput("openai_model") || config.models.openai.model
21
+ const botNameInput = core.getInput("bot_name")
22
+ const fixModeInput = core.getInput("fix_mode")
23
+ const triggerLabelInput = core.getInput("trigger_label", { required: false, trimWhitespace: true })
24
+ const triggerLabelProvided = triggerLabelInput !== ""
25
+
26
+ return {
27
+ mode,
28
+ autoFixEnabled: config.fix.mode !== "propose_only",
29
+ restrictedPaths: config.security.restricted_paths,
30
+ testCommands: config.validation.commands,
31
+ maxFiles: config.review.max_files,
32
+ maxPatchChars: config.review.max_patch_chars,
33
+ reviewRulesMarkdown: undefined,
34
+ architectureNotes: undefined,
35
+ severityThreshold: config.review.severity_threshold as FindingSeverity,
36
+ blockForkMutation: config.security.block_fork_mutation,
37
+ inlineComments: config.review.inline_comments,
38
+ commentStyle: config.review.comment_style,
39
+ models: {
40
+ anthropic: { enabled: config.models.anthropic.enabled, model: anthropicModel },
41
+ openai: { enabled: config.models.openai.enabled, model: openaiModel },
42
+ },
43
+ trigger: {
44
+ requireLabel: triggerLabelProvided ? triggerLabelInput : config.trigger.require_label,
45
+ respondToMentions: config.trigger.respond_to_mentions,
46
+ respondToReplies: config.trigger.respond_to_replies,
47
+ botName: botNameInput || config.trigger.bot_name,
48
+ },
49
+ fix: {
50
+ mode: (fixModeInput as FixMode) || config.fix.mode,
51
+ confidenceThreshold: config.fix.confidence_threshold,
52
+ createDraftPr: config.fix.create_draft_pr,
53
+ maxRetryCount: config.fix.max_retry_count,
54
+ },
55
+ review: {
56
+ summaryOnClean: config.review.summary_on_clean,
57
+ },
58
+ }
59
+ }
60
+
61
+ export function evaluateTrust(ctx: {
62
+ actor: string
63
+ isFork: boolean
64
+ policies: RepoPolicies
65
+ }): { trusted: boolean; canMutate: boolean; reason: string } {
66
+ if (ctx.isFork && ctx.policies.blockForkMutation) {
67
+ return { trusted: false, canMutate: false, reason: "Fork PR — mutation blocked by policy" }
68
+ }
69
+
70
+ if (ctx.policies.mode === "manual_only") {
71
+ return { trusted: true, canMutate: false, reason: "Manual-only mode — review permitted, mutation requires command" }
72
+ }
73
+
74
+ return { trusted: true, canMutate: ctx.policies.autoFixEnabled, reason: "Trusted actor" }
75
+ }
76
+
77
+ export function isRestrictedPath(filePath: string, patterns: string[]): boolean {
78
+ for (const pattern of patterns) {
79
+ if (matchGlob(filePath, pattern)) return true
80
+ }
81
+ return false
82
+ }
83
+
84
+ async function loadConfigFile(
85
+ octokit: Octokit,
86
+ configPath: string
87
+ ): Promise<Record<string, unknown> | null> {
88
+ if (fs.existsSync(configPath)) {
89
+ try {
90
+ const content = fs.readFileSync(configPath, "utf-8")
91
+ return parseYaml(content) as Record<string, unknown>
92
+ } catch (err) {
93
+ core.warning(`Failed to parse local config at ${configPath}: ${err}`)
94
+ }
95
+ }
96
+
97
+ try {
98
+ const { owner, repo } = github.context.repo
99
+ const { data } = await octokit.rest.repos.getContent({
100
+ owner,
101
+ repo,
102
+ path: configPath,
103
+ })
104
+ if ("content" in data && data.content) {
105
+ const decoded = Buffer.from(data.content, "base64").toString("utf-8")
106
+ return parseYaml(decoded) as Record<string, unknown>
107
+ }
108
+ } catch {
109
+ core.info(`No config file found at ${configPath} — using defaults`)
110
+ }
111
+
112
+ return null
113
+ }
114
+
115
+ function parseConfig(raw: Record<string, unknown> | null): SentinelConfig {
116
+ if (!raw) return SentinelConfigSchema.parse({})
117
+
118
+ try {
119
+ return SentinelConfigSchema.parse(raw)
120
+ } catch (err) {
121
+ core.warning(`Config validation failed, using defaults: ${err}`)
122
+ return SentinelConfigSchema.parse({})
123
+ }
124
+ }
125
+
126
+ function matchGlob(path: string, pattern: string): boolean {
127
+ const regex = pattern
128
+ .replace(/\./g, "\\.")
129
+ .replace(/\*\*/g, "{{GLOBSTAR}}")
130
+ .replace(/\*/g, "[^/]*")
131
+ .replace(/\{\{GLOBSTAR\}\}/g, ".*")
132
+ return new RegExp(`^${regex}$`).test(path)
133
+ }