@aryaminus/controlkeel-opencode 0.1.27 → 0.1.28
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/.opencode/agents/controlkeel-operator.md +6 -3
- package/.opencode/commands/controlkeel-review.md +1 -1
- package/.opencode/commands/controlkeel-submit-plan.md +5 -4
- package/.opencode/mcp.json +4 -3
- package/.opencode/plugins/controlkeel-governance.ts +144 -16
- package/index.js +154 -43
- package/package.json +4 -1
|
@@ -11,7 +11,7 @@ and validate them against the project's security, budget, and compliance policie
|
|
|
11
11
|
|
|
12
12
|
## Instructions
|
|
13
13
|
|
|
14
|
-
1. Use
|
|
14
|
+
1. Use `ck_context` first, then `ck_validate` before providing feedback.
|
|
15
15
|
2. Report findings by severity: critical > high > medium > low.
|
|
16
16
|
3. Never approve changes that have unresolved critical or high findings.
|
|
17
17
|
4. Reference specific policy rules when flagging issues.
|
|
@@ -19,7 +19,10 @@ and validate them against the project's security, budget, and compliance policie
|
|
|
19
19
|
|
|
20
20
|
## Available MCP Tools
|
|
21
21
|
|
|
22
|
+
- `ck_context` — Load mission, findings, budget, and proof context
|
|
22
23
|
- `ck_validate` — Run full governance validation
|
|
24
|
+
- `ck_finding` — Record a governed finding when you detect a missed issue
|
|
25
|
+
- `ck_review_submit` — Submit review material for human approval
|
|
26
|
+
- `ck_review_status` — Check review status before execution
|
|
23
27
|
- `ck_budget` — Check remaining budget and spend history
|
|
24
|
-
- `
|
|
25
|
-
- `ck_approve` — Approve a finding (requires operator confirmation)
|
|
28
|
+
- `ck_route` — Ask ControlKeel for the recommended specialist route
|
|
@@ -3,7 +3,7 @@ description: Run ControlKeel governance review on the current project
|
|
|
3
3
|
agent: controlkeel-operator
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Review the current project for governance compliance. Run `
|
|
6
|
+
Review the current project for governance compliance. Run `ck_validate` to check
|
|
7
7
|
for security findings, budget status, and proof readiness. Summarize the results
|
|
8
8
|
and highlight any blockers that need attention before shipping.
|
|
9
9
|
|
|
@@ -6,7 +6,8 @@ Save the current plan to a markdown file, then submit it through ControlKeel.
|
|
|
6
6
|
|
|
7
7
|
Recommended flow:
|
|
8
8
|
1. Save the plan to `.opencode/review-plan.md`
|
|
9
|
-
2.
|
|
10
|
-
3.
|
|
11
|
-
4.
|
|
12
|
-
|
|
9
|
+
2. Ensure `controlkeel version` reports `>= 0.1.26`
|
|
10
|
+
3. Run `controlkeel review plan submit --body-file .opencode/review-plan.md --submitted-by opencode --task-id <task_id> --json` (or use `--session-id <session_id>`)
|
|
11
|
+
4. Read the returned `review.id` and `browser_url`
|
|
12
|
+
5. Wait with `controlkeel review plan wait --id <review_id> --timeout 30 --json`
|
|
13
|
+
6. Do not execute until the review is approved
|
package/.opencode/mcp.json
CHANGED
|
@@ -19,29 +19,151 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const submitPayload = parseJson(result)
|
|
26
|
-
const reviewId = submitPayload?.review?.id
|
|
27
|
-
if (!reviewId) {
|
|
28
|
-
throw new Error("ControlKeel did not return a review id")
|
|
22
|
+
const toText = async (output: unknown) => {
|
|
23
|
+
if (typeof output === "string") {
|
|
24
|
+
return output
|
|
29
25
|
}
|
|
30
|
-
|
|
26
|
+
|
|
27
|
+
if (output == null) {
|
|
28
|
+
return ""
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof output === "object") {
|
|
32
|
+
const stdout = (output as { stdout?: unknown }).stdout
|
|
33
|
+
if (typeof stdout === "string") {
|
|
34
|
+
return stdout
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (stdout && typeof (stdout as { text?: unknown }).text === "function") {
|
|
38
|
+
try {
|
|
39
|
+
const streamed = await (stdout as { text: () => Promise<string> }).text()
|
|
40
|
+
if (typeof streamed === "string") {
|
|
41
|
+
return streamed
|
|
42
|
+
}
|
|
43
|
+
} catch (_error) {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return String(output)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parseVersion = (output: string) => {
|
|
52
|
+
const match = output.match(/(\d+)\.(\d+)\.(\d+)/)
|
|
53
|
+
if (!match) {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
31
57
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
58
|
+
major: Number(match[1]),
|
|
59
|
+
minor: Number(match[2]),
|
|
60
|
+
patch: Number(match[3]),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const versionAtLeast = (
|
|
65
|
+
current: { major: number; minor: number; patch: number },
|
|
66
|
+
required: { major: number; minor: number; patch: number }
|
|
67
|
+
) => {
|
|
68
|
+
if (current.major !== required.major) {
|
|
69
|
+
return current.major > required.major
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (current.minor !== required.minor) {
|
|
73
|
+
return current.minor > required.minor
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return current.patch >= required.patch
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const ensurePlanSubmitSupport = async () => {
|
|
80
|
+
let versionOutput = ""
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
versionOutput = await toText(await $`controlkeel version`)
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"Failed to run `controlkeel version`. Install ControlKeel >= 0.1.26 and ensure `controlkeel` is on PATH."
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const parsed = parseVersion(versionOutput)
|
|
91
|
+
const required = { major: 0, minor: 1, patch: 26 }
|
|
92
|
+
|
|
93
|
+
if (!parsed || !versionAtLeast(parsed, required)) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`ControlKeel CLI ${versionOutput.trim() || "unknown"} is too old for plan-review submit. Install >= 0.1.26.`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const submitPlan = async (
|
|
101
|
+
body: string,
|
|
102
|
+
submittedBy: string,
|
|
103
|
+
title?: string,
|
|
104
|
+
waitTimeoutSeconds?: number
|
|
105
|
+
) => {
|
|
106
|
+
await ensurePlanSubmitSupport()
|
|
107
|
+
|
|
108
|
+
const envTaskId = process.env.CONTROLKEEL_TASK_ID
|
|
109
|
+
const envSessionId = process.env.CONTROLKEEL_SESSION_ID
|
|
110
|
+
const waitTimeout = Number(waitTimeoutSeconds ?? process.env.CONTROLKEEL_REVIEW_WAIT_TIMEOUT ?? 30)
|
|
111
|
+
const waitTimeoutSecondsSafe = Number.isFinite(waitTimeout) && waitTimeout > 0 ? waitTimeout : 30
|
|
112
|
+
|
|
113
|
+
// Write body to temp file to avoid stdin piping issues
|
|
114
|
+
const tmpFile = `${directory}/.opencode/review-plan-${Date.now()}.md`
|
|
115
|
+
await Bun.write(tmpFile, body)
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const submitArgs = ["controlkeel", "review", "plan", "submit", "--body-file", tmpFile, "--submitted-by", submittedBy, "--json"]
|
|
119
|
+
if (title) submitArgs.push("--title", title)
|
|
120
|
+
if (envTaskId) submitArgs.push("--task-id", envTaskId)
|
|
121
|
+
else if (envSessionId) submitArgs.push("--session-id", envSessionId)
|
|
122
|
+
|
|
123
|
+
const submitProc = Bun.spawn(submitArgs, { stdout: "pipe", stderr: "pipe" })
|
|
124
|
+
const submitOut = await new Response(submitProc.stdout).text()
|
|
125
|
+
await submitProc.exited
|
|
126
|
+
|
|
127
|
+
const submitPayload = parseJson(submitOut)
|
|
128
|
+
|
|
129
|
+
if (typeof submitPayload?.error === "string" && submitPayload.error.includes("session_id")) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"ControlKeel plan submission requires review context. Set CONTROLKEEL_TASK_ID (preferred) or CONTROLKEEL_SESSION_ID, or pass --task-id/--session-id manually."
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const reviewId = submitPayload?.review?.id
|
|
136
|
+
if (!reviewId) {
|
|
137
|
+
throw new Error("ControlKeel did not return a review id")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const waitProc = Bun.spawn(["controlkeel", "review", "plan", "wait", "--id", String(reviewId), "--timeout", String(waitTimeoutSecondsSafe), "--json"], { stdout: "pipe", stderr: "pipe" })
|
|
141
|
+
const waitOut = await new Response(waitProc.stdout).text()
|
|
142
|
+
await waitProc.exited
|
|
143
|
+
|
|
144
|
+
const waitPayload = parseJson(waitOut)
|
|
145
|
+
return {
|
|
146
|
+
reviewId,
|
|
147
|
+
submitPayload,
|
|
148
|
+
waitPayload,
|
|
149
|
+
browserUrl: submitPayload?.browser_url,
|
|
150
|
+
status: waitPayload?.review?.status,
|
|
151
|
+
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
152
|
+
}
|
|
153
|
+
} finally {
|
|
154
|
+
// Clean up temp file
|
|
155
|
+
try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
|
|
38
156
|
}
|
|
39
157
|
}
|
|
40
158
|
|
|
41
159
|
return {
|
|
42
|
-
"shell.env": async (
|
|
160
|
+
"shell.env": async (input, output) => {
|
|
43
161
|
output.env.CONTROLKEEL_PROJECT_ROOT = directory
|
|
44
162
|
output.env.CONTROLKEEL_AGENT_ID = "opencode"
|
|
163
|
+
|
|
164
|
+
if (input.sessionID) {
|
|
165
|
+
output.env.CONTROLKEEL_THREAD_ID = input.sessionID
|
|
166
|
+
}
|
|
45
167
|
},
|
|
46
168
|
|
|
47
169
|
config: async (config) => {
|
|
@@ -74,9 +196,15 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
74
196
|
args: {
|
|
75
197
|
plan: tool.schema.string().describe("Markdown plan body to submit for review."),
|
|
76
198
|
title: tool.schema.string().optional(),
|
|
199
|
+
wait_timeout_seconds: tool.schema.number().int().positive().optional(),
|
|
77
200
|
},
|
|
78
201
|
async execute(args) {
|
|
79
|
-
const result = await submitPlan(
|
|
202
|
+
const result = await submitPlan(
|
|
203
|
+
args.plan,
|
|
204
|
+
"opencode",
|
|
205
|
+
args.title,
|
|
206
|
+
args.wait_timeout_seconds
|
|
207
|
+
)
|
|
80
208
|
return JSON.stringify(result, null, 2)
|
|
81
209
|
},
|
|
82
210
|
}),
|
package/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin"
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Published OpenCode package entrypoint for ControlKeel.
|
|
3
5
|
*
|
|
@@ -13,29 +15,143 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
18
|
+
const toText = async (output) => {
|
|
19
|
+
if (typeof output === "string") {
|
|
20
|
+
return output
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (output == null) {
|
|
24
|
+
return ""
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof output === "object") {
|
|
28
|
+
const stdout = output.stdout
|
|
29
|
+
if (typeof stdout === "string") {
|
|
30
|
+
return stdout
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (stdout && typeof stdout.text === "function") {
|
|
34
|
+
try {
|
|
35
|
+
const streamed = await stdout.text()
|
|
36
|
+
if (typeof streamed === "string") {
|
|
37
|
+
return streamed
|
|
38
|
+
}
|
|
39
|
+
} catch (_error) {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return String(output)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const parseVersion = (output) => {
|
|
48
|
+
const match = output.match(/(\d+)\.(\d+)\.(\d+)/)
|
|
49
|
+
if (!match) {
|
|
50
|
+
return null
|
|
23
51
|
}
|
|
24
|
-
|
|
52
|
+
|
|
25
53
|
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
major: Number(match[1]),
|
|
55
|
+
minor: Number(match[2]),
|
|
56
|
+
patch: Number(match[3]),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const versionAtLeast = (current, required) => {
|
|
61
|
+
if (current.major !== required.major) {
|
|
62
|
+
return current.major > required.major
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (current.minor !== required.minor) {
|
|
66
|
+
return current.minor > required.minor
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return current.patch >= required.patch
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ensurePlanSubmitSupport = async () => {
|
|
73
|
+
let versionOutput = ""
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
versionOutput = await toText(await $`controlkeel version`)
|
|
77
|
+
} catch (_error) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Failed to run `controlkeel version`. Install ControlKeel >= 0.1.26 and ensure `controlkeel` is on PATH."
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const parsed = parseVersion(versionOutput)
|
|
84
|
+
const required = { major: 0, minor: 1, patch: 26 }
|
|
85
|
+
|
|
86
|
+
if (!parsed || !versionAtLeast(parsed, required)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`ControlKeel CLI ${versionOutput.trim() || "unknown"} is too old for plan-review submit. Install >= 0.1.26.`
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const submitPlan = async (body, submittedBy, title, waitTimeoutSeconds) => {
|
|
94
|
+
await ensurePlanSubmitSupport()
|
|
95
|
+
|
|
96
|
+
const envTaskId = process.env.CONTROLKEEL_TASK_ID
|
|
97
|
+
const envSessionId = process.env.CONTROLKEEL_SESSION_ID
|
|
98
|
+
const waitTimeout = Number(waitTimeoutSeconds ?? process.env.CONTROLKEEL_REVIEW_WAIT_TIMEOUT ?? 30)
|
|
99
|
+
const waitTimeoutSecondsSafe = Number.isFinite(waitTimeout) && waitTimeout > 0 ? waitTimeout : 30
|
|
100
|
+
|
|
101
|
+
// Write body to temp file to avoid stdin piping issues
|
|
102
|
+
const tmpFile = `${directory}/.opencode/review-plan-${Date.now()}.md`
|
|
103
|
+
await Bun.write(tmpFile, body)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const submitArgs = ["controlkeel", "review", "plan", "submit", "--body-file", tmpFile, "--submitted-by", submittedBy, "--json"]
|
|
107
|
+
if (title) submitArgs.push("--title", title)
|
|
108
|
+
if (envTaskId) submitArgs.push("--task-id", envTaskId)
|
|
109
|
+
else if (envSessionId) submitArgs.push("--session-id", envSessionId)
|
|
110
|
+
|
|
111
|
+
const submitProc = Bun.spawn(submitArgs, { stdout: "pipe", stderr: "pipe" })
|
|
112
|
+
const submitOut = await new Response(submitProc.stdout).text()
|
|
113
|
+
await submitProc.exited
|
|
114
|
+
|
|
115
|
+
const submitPayload = parseJson(submitOut)
|
|
116
|
+
|
|
117
|
+
if (typeof submitPayload?.error === "string" && submitPayload.error.includes("session_id")) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"ControlKeel plan submission requires review context. Set CONTROLKEEL_TASK_ID (preferred) or CONTROLKEEL_SESSION_ID, or pass --task-id/--session-id manually."
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const reviewId = submitPayload?.review?.id
|
|
124
|
+
if (!reviewId) {
|
|
125
|
+
throw new Error("ControlKeel did not return a review id")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const waitProc = Bun.spawn(["controlkeel", "review", "plan", "wait", "--id", String(reviewId), "--timeout", String(waitTimeoutSecondsSafe), "--json"], { stdout: "pipe", stderr: "pipe" })
|
|
129
|
+
const waitOut = await new Response(waitProc.stdout).text()
|
|
130
|
+
await waitProc.exited
|
|
131
|
+
|
|
132
|
+
const waitPayload = parseJson(waitOut)
|
|
133
|
+
return {
|
|
134
|
+
reviewId,
|
|
135
|
+
submitPayload,
|
|
136
|
+
waitPayload,
|
|
137
|
+
browserUrl: submitPayload?.browser_url,
|
|
138
|
+
status: waitPayload?.review?.status,
|
|
139
|
+
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
140
|
+
}
|
|
141
|
+
} finally {
|
|
142
|
+
// Clean up temp file
|
|
143
|
+
try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
|
|
32
144
|
}
|
|
33
145
|
}
|
|
34
146
|
|
|
35
147
|
return {
|
|
36
|
-
"shell.env": async (
|
|
148
|
+
"shell.env": async (input, output) => {
|
|
37
149
|
output.env.CONTROLKEEL_PROJECT_ROOT = directory
|
|
38
150
|
output.env.CONTROLKEEL_AGENT_ID = "opencode"
|
|
151
|
+
|
|
152
|
+
if (input.sessionID) {
|
|
153
|
+
output.env.CONTROLKEEL_THREAD_ID = input.sessionID
|
|
154
|
+
}
|
|
39
155
|
},
|
|
40
156
|
|
|
41
157
|
config: async (config) => {
|
|
@@ -55,37 +171,32 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
55
171
|
}
|
|
56
172
|
},
|
|
57
173
|
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const planBody = typeof input.args?.plan === "string" ? input.args.plan : ""
|
|
64
|
-
const review = await submitPlan(planBody, "opencode")
|
|
65
|
-
output.metadata = {
|
|
66
|
-
reviewId: review.reviewId,
|
|
67
|
-
browserUrl: review.browserUrl,
|
|
68
|
-
status: review.status,
|
|
69
|
-
feedbackNotes: review.feedbackNotes,
|
|
70
|
-
}
|
|
71
|
-
output.result = JSON.stringify(output.metadata, null, 2)
|
|
174
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
175
|
+
output.system.push(
|
|
176
|
+
"Use submit_plan when you are ready for human review. Do not proceed with implementation until ControlKeel approves the plan."
|
|
177
|
+
)
|
|
72
178
|
},
|
|
73
179
|
|
|
74
|
-
|
|
75
|
-
submit_plan: {
|
|
76
|
-
description:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
180
|
+
tool: {
|
|
181
|
+
"submit_plan": tool({
|
|
182
|
+
description:
|
|
183
|
+
"Submit a plan to ControlKeel for browser review. The tool waits for approval before execution continues.",
|
|
184
|
+
args: {
|
|
185
|
+
plan: tool.schema.string().describe("Markdown plan body to submit for review."),
|
|
186
|
+
title: tool.schema.string().optional(),
|
|
187
|
+
wait_timeout_seconds: tool.schema.number().int().positive().optional(),
|
|
188
|
+
},
|
|
189
|
+
async execute(args) {
|
|
190
|
+
const result = await submitPlan(
|
|
191
|
+
args.plan,
|
|
192
|
+
"opencode",
|
|
193
|
+
args.title,
|
|
194
|
+
args.wait_timeout_seconds
|
|
195
|
+
)
|
|
196
|
+
return JSON.stringify(result, null, 2)
|
|
86
197
|
},
|
|
87
|
-
},
|
|
88
|
-
}
|
|
198
|
+
}),
|
|
199
|
+
},
|
|
89
200
|
}
|
|
90
201
|
}
|
|
91
202
|
|
package/package.json
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
"bugs": {
|
|
3
3
|
"url": "https://github.com/aryaminus/controlkeel/issues"
|
|
4
4
|
},
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@opencode-ai/plugin": "1.3.13"
|
|
7
|
+
},
|
|
5
8
|
"description": "ControlKeel OpenCode adapter bundle",
|
|
6
9
|
"exports": {
|
|
7
10
|
".": "./index.js",
|
|
@@ -32,5 +35,5 @@
|
|
|
32
35
|
"url": "git+https://github.com/aryaminus/controlkeel.git"
|
|
33
36
|
},
|
|
34
37
|
"type": "module",
|
|
35
|
-
"version": "0.1.
|
|
38
|
+
"version": "0.1.28"
|
|
36
39
|
}
|