@cobaltio/cobalt-js 9.2.1 → 9.2.2
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/.github/pull_request_template.md +48 -0
- package/.github/workflows/pr-validation.yml +224 -0
- package/CLAUDE.md +10 -0
- package/cobalt.d.ts +2 -1
- package/cobalt.js +5 -1
- package/cobalt.ts +5 -1
- package/docs/classes/Cobalt.html +24 -23
- package/docs/enums/AuthStatus.html +2 -2
- package/docs/enums/AuthType.html +2 -2
- package/docs/interfaces/Application.html +14 -14
- package/docs/interfaces/CobaltOptions.html +3 -3
- package/docs/interfaces/Config.html +2 -2
- package/docs/interfaces/ConfigField.html +4 -4
- package/docs/interfaces/ConfigPayload.html +4 -4
- package/docs/interfaces/ConfigWorkflow.html +2 -2
- package/docs/interfaces/ExecuteWorkflowPayload.html +5 -5
- package/docs/interfaces/Execution.html +2 -2
- package/docs/interfaces/ExecutionFilters.html +8 -8
- package/docs/interfaces/GetExecutionsParams.html +8 -8
- package/docs/interfaces/InputField.html +9 -9
- package/docs/interfaces/Label.html +3 -3
- package/docs/interfaces/PublicWorkflow.html +8 -8
- package/docs/interfaces/PublicWorkflowPayload.html +4 -4
- package/docs/interfaces/PublicWorkflowsPayload.html +6 -6
- package/docs/interfaces/RuleOptions.html +2 -2
- package/docs/interfaces/UpdateConfigPayload.html +5 -5
- package/docs/interfaces/WorkflowPayload.html +4 -4
- package/docs/interfaces/WorkflowPayloadResponse.html +2 -2
- package/docs/llms.txt +195 -194
- package/docs/types/ExecutionSource.html +1 -1
- package/docs/types/ExecutionStatus.html +1 -1
- package/docs/types/ExecutionType.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Paste your Linear ticket ID anywhere in this PR.
|
|
3
|
+
Valid formats: DEV-123, closes DEV-123, resolves DEV-456, fixes AIA-789
|
|
4
|
+
-->
|
|
5
|
+
DEV-
|
|
6
|
+
|
|
7
|
+
### Summary
|
|
8
|
+
<!-- Required. What does this PR do and why? Be specific — mention what changed and the reason for the change. -->
|
|
9
|
+
|
|
10
|
+
### Approach
|
|
11
|
+
<!--
|
|
12
|
+
Optional — include for new features or large changes.
|
|
13
|
+
- What approach did you take and why?
|
|
14
|
+
- What alternatives did you consider and why were they rejected?
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
### Test Plan
|
|
18
|
+
<!--
|
|
19
|
+
Required. Steps for testing these changes — what to do and what to verify.
|
|
20
|
+
At least one checkbox is required.
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
- [ ]
|
|
24
|
+
|
|
25
|
+
### Claude Code
|
|
26
|
+
<!--
|
|
27
|
+
If this PR was built with Claude Code, check all that apply.
|
|
28
|
+
Delete this section entirely for PRs not written with Claude Code.
|
|
29
|
+
-->
|
|
30
|
+
|
|
31
|
+
- [ ] Used `superpowers:brainstorming` before starting implementation
|
|
32
|
+
- [ ] Plan file added to `docs/superpowers/plans/`
|
|
33
|
+
|
|
34
|
+
### Env Changes
|
|
35
|
+
<!--
|
|
36
|
+
Include ONLY if this PR adds, removes, or modifies environment variables.
|
|
37
|
+
Delete this section entirely if there are no env var changes.
|
|
38
|
+
|
|
39
|
+
Format:
|
|
40
|
+
- name: VAR_NAME
|
|
41
|
+
action: add | remove | update
|
|
42
|
+
type: configmap | secret
|
|
43
|
+
value: "example_value" # omit for secrets
|
|
44
|
+
description: What this var does and where it's used
|
|
45
|
+
-->
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
```
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
name: PR Validation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [uat]
|
|
6
|
+
types: [opened, edited, synchronize, reopened]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pr-validation:
|
|
10
|
+
name: pr-validation
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
pull-requests: write
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Validate PR template
|
|
18
|
+
uses: actions/github-script@v7
|
|
19
|
+
with:
|
|
20
|
+
script: |
|
|
21
|
+
const pr = context.payload.pull_request;
|
|
22
|
+
const title = pr.title || '';
|
|
23
|
+
const body = pr.body || '';
|
|
24
|
+
const labels = pr.labels.map(l => l.name);
|
|
25
|
+
const isHotfix = labels.includes('hotfix');
|
|
26
|
+
|
|
27
|
+
const errors = [];
|
|
28
|
+
const warnings = [];
|
|
29
|
+
|
|
30
|
+
function section(heading) {
|
|
31
|
+
const esc = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
32
|
+
const re = new RegExp(`#{1,6}\\s+${esc}\\s*\\n([\\s\\S]*?)(?=\\n#{1,6}\\s|$)`, 'i');
|
|
33
|
+
const m = body.match(re);
|
|
34
|
+
if (!m) return null;
|
|
35
|
+
return m[1].replace(/<!--[\s\\S]*?-->/g, '').trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isHotfix) {
|
|
39
|
+
warnings.push(
|
|
40
|
+
'This PR is labelled **hotfix** — template checks skipped. ' +
|
|
41
|
+
'Tests are still required.'
|
|
42
|
+
);
|
|
43
|
+
} else {
|
|
44
|
+
const titleRe = /^(feat|fix|chore|refactor|build|perf|breaking|docs|internal)(\(.+\))?: .{3,}/;
|
|
45
|
+
const isInternal = /^internal(\(.+\))?:/.test(title);
|
|
46
|
+
if (!titleRe.test(title)) {
|
|
47
|
+
errors.push(
|
|
48
|
+
'**Title format** — must match `type(scope): short description`\n' +
|
|
49
|
+
' Valid types: `feat` `fix` `chore` `refactor` `build` `perf` `breaking` `docs` `internal`\n' +
|
|
50
|
+
' Example: `fix(auth): resolve token expiry on refresh`\n' +
|
|
51
|
+
' Use `internal` for tooling/process PRs that have no Linear ticket\n' +
|
|
52
|
+
` Got: \`${title}\``
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const summary = section('Summary') || '';
|
|
57
|
+
if (summary.length < 20) {
|
|
58
|
+
errors.push(
|
|
59
|
+
`**Summary** — must be at least 20 characters (got ${summary.length})`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!isInternal && !/^\w+\s+[A-Z]+-\d+\b|^[A-Z]+-\d+\b/m.test(body)) {
|
|
64
|
+
errors.push(
|
|
65
|
+
'**Linear ticket** — a valid ticket ID must appear on its own line in the PR body\n' +
|
|
66
|
+
' Valid formats: `DEV-123`, `closes DEV-123`, `resolves AIA-456`, `fixes DEV-789`\n' +
|
|
67
|
+
' If there is no ticket, use `internal` as the title type to skip this check'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const testPlan = section('Test Plan') || '';
|
|
72
|
+
if (!/- \[ \] .+/.test(testPlan)) {
|
|
73
|
+
errors.push(
|
|
74
|
+
'**Test Plan** — must include at least one step with text, e.g. `- [ ] Navigate to X and verify Y`'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let prFiles = [];
|
|
79
|
+
try {
|
|
80
|
+
const { data: files } = await github.rest.pulls.listFiles({
|
|
81
|
+
owner: context.repo.owner,
|
|
82
|
+
repo: context.repo.repo,
|
|
83
|
+
pull_number: pr.number,
|
|
84
|
+
per_page: 100,
|
|
85
|
+
});
|
|
86
|
+
prFiles = files;
|
|
87
|
+
} catch (e) {
|
|
88
|
+
core.warning(`Could not list PR files: ${e.message}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const needsPlan = /^(feat|refactor|breaking)(\(.+\))?:/.test(title);
|
|
92
|
+
const hasPlanFile = prFiles.some(f =>
|
|
93
|
+
f.filename.startsWith('docs/superpowers/plans/') ||
|
|
94
|
+
f.filename.startsWith('docs/superpowers/specs/')
|
|
95
|
+
);
|
|
96
|
+
if (needsPlan && !hasPlanFile) {
|
|
97
|
+
warnings.push(
|
|
98
|
+
'**Superpowers plan file missing** — feature PRs should include a plan file in `docs/superpowers/plans/`.\n' +
|
|
99
|
+
' Run `superpowers:brainstorming` + `superpowers:writing-plans` in Claude Code before implementing.\n' +
|
|
100
|
+
' If you intentionally skipped planning (trivial change), you can ignore this warning.'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const envPatterns = [
|
|
105
|
+
/\.env(\.|$)/i,
|
|
106
|
+
/(^|\/)\.env$/i,
|
|
107
|
+
/(^|\/)env\//i,
|
|
108
|
+
/(^|\/)config\//i,
|
|
109
|
+
/helm\/.*values.*\.ya?ml$/i,
|
|
110
|
+
/docker-compose.*\.ya?ml$/i,
|
|
111
|
+
/kubernetes\/.*\.ya?ml$/i,
|
|
112
|
+
/k8s\/.*\.ya?ml$/i,
|
|
113
|
+
];
|
|
114
|
+
const touchesEnvFiles = prFiles.some(f =>
|
|
115
|
+
envPatterns.some(p => p.test(f.filename))
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (touchesEnvFiles) {
|
|
119
|
+
const envSection = section('Env Changes') || '';
|
|
120
|
+
const hasContent =
|
|
121
|
+
/none/i.test(envSection) ||
|
|
122
|
+
/- name:\s*\S+/.test(envSection);
|
|
123
|
+
if (!hasContent) {
|
|
124
|
+
errors.push(
|
|
125
|
+
'**Env Changes** — this PR modifies config/env files but ' +
|
|
126
|
+
'the Env Changes section is empty.\n' +
|
|
127
|
+
' Document all env variable changes, or write `none` if no variables changed.'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const MARKER = '<!-- pr-validation-bot -->';
|
|
134
|
+
if (errors.length > 0 || warnings.length > 0) {
|
|
135
|
+
const lines = [MARKER];
|
|
136
|
+
if (isHotfix) {
|
|
137
|
+
lines.push('### ⚠️ PR Validation — Hotfix Mode');
|
|
138
|
+
lines.push('');
|
|
139
|
+
warnings.forEach(w => lines.push(`> ${w}`));
|
|
140
|
+
} else {
|
|
141
|
+
if (errors.length > 0) {
|
|
142
|
+
lines.push('### ⚠️ PR Validation Failed');
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push(
|
|
145
|
+
`The following ${errors.length === 1 ? 'field requires' : `${errors.length} fields require`} attention:`
|
|
146
|
+
);
|
|
147
|
+
lines.push('');
|
|
148
|
+
errors.forEach(e => lines.push(`- ${e}`));
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push('_Fix the above and the check will re-run automatically._');
|
|
151
|
+
}
|
|
152
|
+
if (warnings.length > 0) {
|
|
153
|
+
if (errors.length > 0) lines.push('');
|
|
154
|
+
lines.push(errors.length > 0 ? '### ⚠️ Warnings' : '### ⚠️ PR Validation — Warnings');
|
|
155
|
+
lines.push('');
|
|
156
|
+
warnings.forEach(w => lines.push(`> ${w}`));
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push('_Warnings will not fail the check — address them if applicable._');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const commentBody = lines.join('\n');
|
|
163
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
164
|
+
owner: context.repo.owner,
|
|
165
|
+
repo: context.repo.repo,
|
|
166
|
+
issue_number: pr.number,
|
|
167
|
+
});
|
|
168
|
+
const existing = comments.find(
|
|
169
|
+
c => c.user.type === 'Bot' && c.body.includes(MARKER)
|
|
170
|
+
);
|
|
171
|
+
if (existing) {
|
|
172
|
+
await github.rest.issues.updateComment({
|
|
173
|
+
owner: context.repo.owner,
|
|
174
|
+
repo: context.repo.repo,
|
|
175
|
+
comment_id: existing.id,
|
|
176
|
+
body: commentBody,
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
await github.rest.issues.createComment({
|
|
180
|
+
owner: context.repo.owner,
|
|
181
|
+
repo: context.repo.repo,
|
|
182
|
+
issue_number: pr.number,
|
|
183
|
+
body: commentBody,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (errors.length > 0) {
|
|
189
|
+
core.setFailed(
|
|
190
|
+
`PR validation failed — ${errors.length} field(s) need attention. ` +
|
|
191
|
+
`See PR comment for details.`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
- name: Notify Slack on failure
|
|
196
|
+
if: failure()
|
|
197
|
+
env:
|
|
198
|
+
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
199
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
200
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
201
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
202
|
+
PR_BRANCH: ${{ github.head_ref }}
|
|
203
|
+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
|
204
|
+
run: |
|
|
205
|
+
payload=$(jq -n \
|
|
206
|
+
--arg url "$PR_URL" \
|
|
207
|
+
--arg number "$PR_NUMBER" \
|
|
208
|
+
--arg title "$PR_TITLE" \
|
|
209
|
+
--arg branch "$PR_BRANCH" \
|
|
210
|
+
--arg author "$PR_AUTHOR" \
|
|
211
|
+
'{
|
|
212
|
+
text: ":warning: *PR Validation Failed* — cobalt-js",
|
|
213
|
+
attachments: [{
|
|
214
|
+
color: "warning",
|
|
215
|
+
fields: [
|
|
216
|
+
{ title: "PR", value: ("<" + $url + "|#" + $number + " " + $title + ">"), short: false },
|
|
217
|
+
{ title: "Branch", value: ("`" + $branch + "`"), short: true },
|
|
218
|
+
{ title: "Author", value: $author, short: true }
|
|
219
|
+
]
|
|
220
|
+
}]
|
|
221
|
+
}')
|
|
222
|
+
curl -s -X POST "$SLACK_WEBHOOK" \
|
|
223
|
+
-H "Content-Type: application/json" \
|
|
224
|
+
-d "$payload"
|
package/CLAUDE.md
CHANGED
|
@@ -115,3 +115,13 @@ All requests include `Authorization: Bearer ${token}`:
|
|
|
115
115
|
- **v9.x:** Added `getWorkflowPayload()`, `executeWorkflow()`, multi-auth support
|
|
116
116
|
- **v8.x:** Introduced `AuthType` enum for multi-auth
|
|
117
117
|
- Deprecated fields maintained for backward compatibility
|
|
118
|
+
|
|
119
|
+
## Claude Code Skills
|
|
120
|
+
|
|
121
|
+
All development using Claude Code must use the superpowers skills plugin. Required before every task:
|
|
122
|
+
|
|
123
|
+
- **Before building features or components:** invoke `superpowers:brainstorming` to explore intent and design first
|
|
124
|
+
- **Before multi-step implementation:** invoke `superpowers:writing-plans` — this saves a plan to `docs/superpowers/plans/`
|
|
125
|
+
- **Before claiming work is done:** invoke `superpowers:verification-before-completion` before committing or opening a PR
|
|
126
|
+
|
|
127
|
+
For feature work (`feat/*` branches), include the plan file from `docs/superpowers/plans/` in the PR. PRs without a plan file for feature branches will receive a warning from the PR validation bot.
|
package/cobalt.d.ts
CHANGED
|
@@ -457,9 +457,10 @@ declare class Cobalt {
|
|
|
457
457
|
* @param {String} slug The application slug.
|
|
458
458
|
* @param {String} fieldId The unique ID of the field.
|
|
459
459
|
* @param {String} [workflowId] The unique ID of the workflow.
|
|
460
|
+
* @param {Record<string, unknown>} [payload] The payload to be sent in the request body.
|
|
460
461
|
* @returns {Promise<Field>} The specified config field.
|
|
461
462
|
*/
|
|
462
|
-
getConfigField(slug: string, fieldId: string, workflowId?: string): Promise<Config>;
|
|
463
|
+
getConfigField(slug: string, fieldId: string, workflowId?: string, payload?: Record<string, unknown>): Promise<Config>;
|
|
463
464
|
/**
|
|
464
465
|
* Update the specified config field value.
|
|
465
466
|
* @param {String} slug The application slug.
|
package/cobalt.js
CHANGED
|
@@ -380,15 +380,19 @@ class Cobalt {
|
|
|
380
380
|
* @param {String} slug The application slug.
|
|
381
381
|
* @param {String} fieldId The unique ID of the field.
|
|
382
382
|
* @param {String} [workflowId] The unique ID of the workflow.
|
|
383
|
+
* @param {Record<string, unknown>} [payload] The payload to be sent in the request body.
|
|
383
384
|
* @returns {Promise<Field>} The specified config field.
|
|
384
385
|
*/
|
|
385
|
-
getConfigField(slug, fieldId, workflowId) {
|
|
386
|
+
getConfigField(slug, fieldId, workflowId, payload) {
|
|
386
387
|
return __awaiter(this, void 0, void 0, function* () {
|
|
387
388
|
const res = yield fetch(`${this.baseUrl}/api/v2/public/config/field/${fieldId}${workflowId ? `?workflow_id=${workflowId}` : ""}`, {
|
|
389
|
+
method: "POST",
|
|
388
390
|
headers: {
|
|
389
391
|
authorization: `Bearer ${this.token}`,
|
|
392
|
+
"content-type": "application/json",
|
|
390
393
|
slug,
|
|
391
394
|
},
|
|
395
|
+
body: JSON.stringify(payload || {}),
|
|
392
396
|
});
|
|
393
397
|
if (res.status >= 400 && res.status < 600) {
|
|
394
398
|
const error = yield res.json();
|
package/cobalt.ts
CHANGED
|
@@ -749,14 +749,18 @@ class Cobalt {
|
|
|
749
749
|
* @param {String} slug The application slug.
|
|
750
750
|
* @param {String} fieldId The unique ID of the field.
|
|
751
751
|
* @param {String} [workflowId] The unique ID of the workflow.
|
|
752
|
+
* @param {Record<string, unknown>} [payload] The payload to be sent in the request body.
|
|
752
753
|
* @returns {Promise<Field>} The specified config field.
|
|
753
754
|
*/
|
|
754
|
-
async getConfigField(slug: string, fieldId: string, workflowId?: string): Promise<Config> {
|
|
755
|
+
async getConfigField(slug: string, fieldId: string, workflowId?: string, payload?: Record<string, unknown>): Promise<Config> {
|
|
755
756
|
const res = await fetch(`${this.baseUrl}/api/v2/public/config/field/${fieldId}${workflowId ? `?workflow_id=${workflowId}` : ""}`, {
|
|
757
|
+
method: "POST",
|
|
756
758
|
headers: {
|
|
757
759
|
authorization: `Bearer ${this.token}`,
|
|
760
|
+
"content-type": "application/json",
|
|
758
761
|
slug,
|
|
759
762
|
},
|
|
763
|
+
body: JSON.stringify(payload || {}),
|
|
760
764
|
});
|
|
761
765
|
|
|
762
766
|
if (res.status >= 400 && res.status < 600) {
|