@cobaltio/cobalt-js 9.2.0 → 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.
Files changed (33) hide show
  1. package/.github/pull_request_template.md +48 -0
  2. package/.github/workflows/pr-validation.yml +224 -0
  3. package/CLAUDE.md +10 -0
  4. package/cobalt.d.ts +4 -2
  5. package/cobalt.js +8 -5
  6. package/cobalt.ts +9 -3
  7. package/docs/classes/Cobalt.html +25 -23
  8. package/docs/enums/AuthStatus.html +2 -2
  9. package/docs/enums/AuthType.html +2 -2
  10. package/docs/interfaces/Application.html +14 -14
  11. package/docs/interfaces/CobaltOptions.html +3 -3
  12. package/docs/interfaces/Config.html +2 -2
  13. package/docs/interfaces/ConfigField.html +4 -4
  14. package/docs/interfaces/ConfigPayload.html +4 -4
  15. package/docs/interfaces/ConfigWorkflow.html +2 -2
  16. package/docs/interfaces/ExecuteWorkflowPayload.html +5 -5
  17. package/docs/interfaces/Execution.html +2 -2
  18. package/docs/interfaces/ExecutionFilters.html +8 -8
  19. package/docs/interfaces/GetExecutionsParams.html +8 -8
  20. package/docs/interfaces/InputField.html +9 -9
  21. package/docs/interfaces/Label.html +3 -3
  22. package/docs/interfaces/PublicWorkflow.html +8 -8
  23. package/docs/interfaces/PublicWorkflowPayload.html +4 -4
  24. package/docs/interfaces/PublicWorkflowsPayload.html +6 -6
  25. package/docs/interfaces/RuleOptions.html +2 -2
  26. package/docs/interfaces/UpdateConfigPayload.html +5 -5
  27. package/docs/interfaces/WorkflowPayload.html +4 -4
  28. package/docs/interfaces/WorkflowPayloadResponse.html +2 -2
  29. package/docs/llms.txt +197 -195
  30. package/docs/types/ExecutionSource.html +1 -1
  31. package/docs/types/ExecutionStatus.html +1 -1
  32. package/docs/types/ExecutionType.html +1 -1
  33. 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
@@ -435,9 +435,10 @@ declare class Cobalt {
435
435
  * Returns the specified config.
436
436
  * @param {String} slug The application slug.
437
437
  * @param {String} [configId] The unique ID of the config.
438
+ * @param {Boolean} [excludeOptions] Whether to exclude the options from the fields in the response.
438
439
  * @returns {Promise<Config>} The specified config.
439
440
  */
440
- getConfig(slug: string, configId: string): Promise<Config>;
441
+ getConfig(slug: string, configId: string, excludeOptions?: boolean): Promise<Config>;
441
442
  /**
442
443
  * Update the specified config.
443
444
  * @param {UpdateConfigPayload} payload The update payload.
@@ -456,9 +457,10 @@ declare class Cobalt {
456
457
  * @param {String} slug The application slug.
457
458
  * @param {String} fieldId The unique ID of the field.
458
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.
459
461
  * @returns {Promise<Field>} The specified config field.
460
462
  */
461
- getConfigField(slug: string, fieldId: string, workflowId?: string): Promise<Config>;
463
+ getConfigField(slug: string, fieldId: string, workflowId?: string, payload?: Record<string, unknown>): Promise<Config>;
462
464
  /**
463
465
  * Update the specified config field value.
464
466
  * @param {String} slug The application slug.
package/cobalt.js CHANGED
@@ -317,14 +317,13 @@ class Cobalt {
317
317
  * Returns the specified config.
318
318
  * @param {String} slug The application slug.
319
319
  * @param {String} [configId] The unique ID of the config.
320
+ * @param {Boolean} [excludeOptions] Whether to exclude the options from the fields in the response.
320
321
  * @returns {Promise<Config>} The specified config.
321
322
  */
322
- getConfig(slug, configId) {
323
+ getConfig(slug, configId, excludeOptions) {
323
324
  return __awaiter(this, void 0, void 0, function* () {
324
325
  const res = yield fetch(`${this.baseUrl}/api/v2/f-sdk/slug/${slug}/config${configId ? `/${configId}` : ""}`, {
325
- headers: {
326
- authorization: `Bearer ${this.token}`,
327
- },
326
+ headers: Object.assign({ authorization: `Bearer ${this.token}` }, (excludeOptions ? { disable_field_options: "true" } : {})),
328
327
  });
329
328
  if (res.status >= 400 && res.status < 600) {
330
329
  const error = yield res.json();
@@ -381,15 +380,19 @@ class Cobalt {
381
380
  * @param {String} slug The application slug.
382
381
  * @param {String} fieldId The unique ID of the field.
383
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.
384
384
  * @returns {Promise<Field>} The specified config field.
385
385
  */
386
- getConfigField(slug, fieldId, workflowId) {
386
+ getConfigField(slug, fieldId, workflowId, payload) {
387
387
  return __awaiter(this, void 0, void 0, function* () {
388
388
  const res = yield fetch(`${this.baseUrl}/api/v2/public/config/field/${fieldId}${workflowId ? `?workflow_id=${workflowId}` : ""}`, {
389
+ method: "POST",
389
390
  headers: {
390
391
  authorization: `Bearer ${this.token}`,
392
+ "content-type": "application/json",
391
393
  slug,
392
394
  },
395
+ body: JSON.stringify(payload || {}),
393
396
  });
394
397
  if (res.status >= 400 && res.status < 600) {
395
398
  const error = yield res.json();
package/cobalt.ts CHANGED
@@ -680,12 +680,14 @@ class Cobalt {
680
680
  * Returns the specified config.
681
681
  * @param {String} slug The application slug.
682
682
  * @param {String} [configId] The unique ID of the config.
683
+ * @param {Boolean} [excludeOptions] Whether to exclude the options from the fields in the response.
683
684
  * @returns {Promise<Config>} The specified config.
684
685
  */
685
- async getConfig(slug: string, configId: string): Promise<Config> {
686
+ async getConfig(slug: string, configId: string, excludeOptions?: boolean): Promise<Config> {
686
687
  const res = await fetch(`${this.baseUrl}/api/v2/f-sdk/slug/${slug}/config${configId ? `/${configId}` : ""}`, {
687
688
  headers: {
688
689
  authorization: `Bearer ${this.token}`,
690
+ ...(excludeOptions ? { disable_field_options: "true" } : {}),
689
691
  },
690
692
  });
691
693
 
@@ -747,14 +749,18 @@ class Cobalt {
747
749
  * @param {String} slug The application slug.
748
750
  * @param {String} fieldId The unique ID of the field.
749
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.
750
753
  * @returns {Promise<Field>} The specified config field.
751
754
  */
752
- 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> {
753
756
  const res = await fetch(`${this.baseUrl}/api/v2/public/config/field/${fieldId}${workflowId ? `?workflow_id=${workflowId}` : ""}`, {
757
+ method: "POST",
754
758
  headers: {
755
759
  authorization: `Bearer ${this.token}`,
760
+ "content-type": "application/json",
756
761
  slug,
757
762
  },
763
+ body: JSON.stringify(payload || {}),
758
764
  });
759
765
 
760
766
  if (res.status >= 400 && res.status < 600) {
@@ -863,7 +869,7 @@ class Cobalt {
863
869
  const value = rest[key];
864
870
  if (value !== undefined && value !== "") query.set(key, String(value));
865
871
  }
866
-
872
+
867
873
  const res = await fetch(`${this.baseUrl}/api/v2/public/workflow?${query}`, {
868
874
  headers: {
869
875
  authorization: `Bearer ${this.token}`,