corifeus-builder 2025.4.135 → 2026.4.138

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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
  ---
8
- # 🏗️ Corifeus Builder v2025.4.135
8
+ # 🏗️ Corifeus Builder v2026.4.138
9
9
 
10
10
 
11
11
 
@@ -21,7 +21,7 @@
21
21
  ### 🛠️ Built on NodeJs version
22
22
 
23
23
  ```txt
24
- v22.13.1
24
+ v24.14.1
25
25
  ```
26
26
 
27
27
 
@@ -155,7 +155,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
155
155
  ---
156
156
 
157
157
 
158
- [**CORIFEUS-BUILDER**](https://corifeus.com/corifeus-builder) Build v2025.4.135
158
+ [**CORIFEUS-BUILDER**](https://corifeus.com/corifeus-builder) Build v2026.4.138
159
159
 
160
160
  [![NPM](https://img.shields.io/npm/v/corifeus-builder.svg)](https://www.npmjs.com/package/corifeus-builder) [![Donate for PatrikX3 / P3X](https://img.shields.io/badge/Donate-PatrikX3-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software)
161
161
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "corifeus-builder",
3
- "version": "2025.4.135",
3
+ "version": "2026.4.138",
4
4
  "corifeus": {
5
5
  "icon": "fas fa-gavel",
6
6
  "code": "Make",
7
7
  "publish": true,
8
- "nodejs": "v22.13.1",
8
+ "nodejs": "v24.14.1",
9
9
  "reponame": "corifeus-builder",
10
10
  "opencollective": false,
11
11
  "build": true
@@ -33,10 +33,10 @@
33
33
  },
34
34
  "homepage": "https://corifeus.com/corifeus-builder",
35
35
  "dependencies": {
36
- "corifeus-utils": "^2025.4.120",
36
+ "corifeus-utils": "^2025.4.123",
37
37
  "download": "^8.0.0",
38
38
  "extract-zip": "^2.0.1",
39
- "fs-extra": "^11.3.0",
39
+ "fs-extra": "^11.3.4",
40
40
  "github-api": "^3.4.0",
41
41
  "glob": "^8.0.3",
42
42
  "glob-promise": "^6.0.7",
@@ -46,15 +46,15 @@
46
46
  "grunt-contrib-htmlmin": "^3.1.0",
47
47
  "grunt-contrib-watch": "^1.1.0",
48
48
  "jit-grunt": "^0.10.0",
49
- "lodash": "^4.17.21",
49
+ "lodash": "^4.18.1",
50
50
  "mkdirp": "^3.0.1",
51
- "mocha": "^11.1.0",
51
+ "mocha": "^11.7.5",
52
52
  "mz": "^2.7.0",
53
- "npm": "^11.1.0",
54
- "npm-check-updates": "^17.1.14",
53
+ "npm": "^11.12.1",
54
+ "npm-check-updates": "^20.0.0",
55
55
  "should": "^13.2.3",
56
56
  "time-grunt": "^2.0.0",
57
- "yaml": "^2.7.0"
57
+ "yaml": "^2.8.3"
58
58
  },
59
59
  "devDependencies": {
60
60
  "twemoji": "^14.0.2"
package/src/loader.js CHANGED
@@ -43,7 +43,11 @@ class loader {
43
43
  require('./replaces')(options, pkg);
44
44
 
45
45
  grunt.config.merge(config);
46
- Object.keys(task).forEach((taskItem) => task[taskItem](grunt))
46
+ Object.keys(task).forEach((taskItem) => {
47
+ if (typeof task[taskItem] === 'function') {
48
+ task[taskItem](grunt)
49
+ }
50
+ })
47
51
 
48
52
  grunt.registerTask('cory:kill', function () {
49
53
  process.exit(1);
@@ -0,0 +1,263 @@
1
+ const fs = require('fs').promises
2
+ const fsSync = require('fs')
3
+ const path = require('path')
4
+ const { execSync } = require('child_process')
5
+
6
+ const getCommitsSinceLastBump = (repoDir) => {
7
+ try {
8
+ const bumps = execSync(
9
+ 'git log --oneline --grep="bump version" --grep="chore: bump" --grep="chore(release):" --format="%H" -2',
10
+ { cwd: repoDir, encoding: 'utf-8' }
11
+ ).trim().split('\n').filter(Boolean)
12
+
13
+ if (bumps.length >= 1) {
14
+ const afterBump = execSync(
15
+ `git log --oneline --no-merges ${bumps[0]}..HEAD`,
16
+ { cwd: repoDir, encoding: 'utf-8' }
17
+ ).trim()
18
+ if (afterBump) return afterBump
19
+ }
20
+
21
+ if (bumps.length >= 2) {
22
+ const log = execSync(
23
+ `git log --oneline --no-merges ${bumps[1]}..${bumps[0]}`,
24
+ { cwd: repoDir, encoding: 'utf-8' }
25
+ ).trim()
26
+ if (log) return log
27
+ }
28
+ } catch (e) {}
29
+
30
+ try {
31
+ return execSync('git log --oneline --no-merges -20', { cwd: repoDir, encoding: 'utf-8' }).trim()
32
+ } catch (e) {
33
+ return ''
34
+ }
35
+ }
36
+
37
+ const getLastPublishedVersion = async (packageName, repoDir) => {
38
+ const nodeModulesPkgPath = path.resolve(repoDir, 'node_modules', packageName, 'package.json')
39
+ try {
40
+ const content = await fs.readFile(nodeModulesPkgPath, 'utf8')
41
+ return JSON.parse(content).version
42
+ } catch (e) {
43
+ return null
44
+ }
45
+ }
46
+
47
+ const getCommitForVersion = (repoDir, version) => {
48
+ try {
49
+ const commit = execSync(
50
+ `git log --all --oneline --grep="v${version}" --grep="${version}" --format="%H" -1`,
51
+ { cwd: repoDir, encoding: 'utf-8' }
52
+ ).trim()
53
+ if (commit) return commit
54
+ } catch (e) {}
55
+
56
+ try {
57
+ const commit = execSync(
58
+ `git log --all --oneline -S '"version": "${version}"' --format="%H" -- package.json | tail -1`,
59
+ { cwd: repoDir, encoding: 'utf-8' }
60
+ ).trim()
61
+ if (commit) return commit
62
+ } catch (e) {}
63
+
64
+ return null
65
+ }
66
+
67
+ /**
68
+ * Collect git logs from multiple repos.
69
+ *
70
+ * @param {Object} options
71
+ * @param {string} options.cwd - project root
72
+ * @param {Array<{dir: string, npmName?: string}>} [options.repos] - workspace repos (defaults to single repo)
73
+ */
74
+ const collectLogs = async (options) => {
75
+ const { cwd, repos } = options
76
+
77
+ if (!repos || repos.length === 0) {
78
+ const log = getCommitsSinceLastBump(cwd)
79
+ return log ? [log] : []
80
+ }
81
+
82
+ const logs = []
83
+ for (const repoCfg of repos) {
84
+ const repoDir = path.resolve(cwd, repoCfg.dir)
85
+ try {
86
+ if (repoCfg.npmName) {
87
+ const bumpLog = getCommitsSinceLastBump(repoDir)
88
+ if (bumpLog) {
89
+ logs.push(`## ${repoCfg.dir}\n${bumpLog}`)
90
+ continue
91
+ }
92
+
93
+ const lastPublishedVersion = await getLastPublishedVersion(repoCfg.npmName, cwd)
94
+ let sinceCommit = null
95
+ if (lastPublishedVersion) {
96
+ sinceCommit = getCommitForVersion(repoDir, lastPublishedVersion)
97
+ }
98
+
99
+ let logCmd
100
+ if (sinceCommit) {
101
+ logCmd = `git log --oneline --no-merges ${sinceCommit}..HEAD`
102
+ } else {
103
+ let lastTag = ''
104
+ try {
105
+ lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { cwd: repoDir, encoding: 'utf-8' }).trim()
106
+ } catch (e) {}
107
+ logCmd = lastTag ? `git log --oneline --no-merges ${lastTag}..HEAD` : 'git log --oneline --no-merges -20'
108
+ }
109
+ const log = execSync(logCmd, { cwd: repoDir, encoding: 'utf-8' }).trim()
110
+ if (log) logs.push(`## ${repoCfg.dir}\n${log}`)
111
+ } else {
112
+ const bumpLog = getCommitsSinceLastBump(repoDir)
113
+ if (bumpLog) {
114
+ logs.push(`## ${repoCfg.dir}\n${bumpLog}`)
115
+ continue
116
+ }
117
+ let lastTag = ''
118
+ try {
119
+ lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { cwd: repoDir, encoding: 'utf-8' }).trim()
120
+ } catch (e) {}
121
+ const logCmd = lastTag ? `git log --oneline --no-merges ${lastTag}..HEAD` : 'git log --oneline --no-merges -20'
122
+ const log = execSync(logCmd, { cwd: repoDir, encoding: 'utf-8' }).trim()
123
+ if (log) logs.push(`## ${repoCfg.dir}\n${log}`)
124
+ }
125
+ } catch (e) {
126
+ console.error(`Failed to get git log for ${repoCfg.dir}:`, e.message)
127
+ }
128
+ }
129
+ return logs
130
+ }
131
+
132
+ /**
133
+ * Generate changelog entry using Claude AI and update change-log.md
134
+ *
135
+ * @param {Object} options
136
+ * @param {string} options.cwd - project root
137
+ * @param {string} options.projectName - e.g. "P3X Network MCP" or "P3X Redis UI"
138
+ * @param {Array<{dir: string, npmName?: string}>} [options.repos] - workspace repos
139
+ */
140
+ const generateChangelog = async (options) => {
141
+ const { cwd, projectName, repos } = options
142
+ const pkg = JSON.parse(await fs.readFile(path.resolve(cwd, 'package.json'), 'utf-8'))
143
+ const version = pkg.version
144
+ const today = new Date().toLocaleDateString('en-US', { month: '2-digit', day: '2-digit', year: 'numeric' })
145
+ const changelogPath = path.resolve(cwd, 'change-log.md')
146
+
147
+ const logs = await collectLogs({ cwd, repos })
148
+ const allLogs = logs.join('\n\n')
149
+ if (!allLogs) {
150
+ console.log('No new commits for changelog')
151
+ return
152
+ }
153
+
154
+ const existingChangelog = await fs.readFile(changelogPath, 'utf-8')
155
+ let previousEntry = ''
156
+ const prevMatch = existingChangelog.match(/### v[\d.]+\nReleased on [^\n]+\n([\s\S]*?)(?=\n### v|\n$)/)
157
+ if (prevMatch) {
158
+ previousEntry = prevMatch[0]
159
+ }
160
+
161
+ let changelogEntry = ''
162
+ try {
163
+ const repoNote = repos && repos.length > 1
164
+ ? `- The commits come from MULTIPLE repos (${repos.map(r => r.dir).join(', ')}) — include significant changes from ALL repos, not just the parent`
165
+ : ''
166
+
167
+ const prompt = `Generate a changelog entry for version v${version} of ${projectName}.
168
+ Based on these recent git commits, write changelog bullet points.
169
+
170
+ ${allLogs}
171
+
172
+ IMPORTANT: The following is the PREVIOUS version's changelog entry. Do NOT repeat any of these items. Only include changes that are NEW in this version:
173
+
174
+ ${previousEntry}
175
+
176
+ Rules:
177
+ - Use this EXACT format:
178
+ ### v${version}
179
+ Released on ${today}
180
+ * CATEGORY: Description.
181
+
182
+ - Valid categories: FEATURE, BUGFIX, PERF, REFACTOR, DOCS, CHORE
183
+ - Each bullet starts with "* CATEGORY: " (with the asterisk)
184
+ - Only include user-facing or significant changes
185
+ - SKIP these types of commits entirely:
186
+ - Version bumps, submodule updates, typo fixes, changelog updates
187
+ - Anything related to the secure/ folder
188
+ - Internal CI/CD, build pipeline, or release automation changes
189
+ - Co-Authored-By lines or merge commits
190
+ - Keep descriptions concise (one line each)
191
+ - Do NOT repeat anything from the previous changelog entry shown above
192
+ - Do NOT use markdown code fences
193
+ - Output ONLY the changelog entry starting with ### — absolutely NO extra text
194
+ ${repoNote}
195
+ - If there are many feature commits, list each feature separately`
196
+
197
+ const tmpPrompt = path.resolve(cwd, '.changelog-prompt.tmp')
198
+ await fs.writeFile(tmpPrompt, prompt)
199
+ changelogEntry = execSync(
200
+ `cat ${JSON.stringify(tmpPrompt)} | claude -p - --no-session-persistence`,
201
+ { encoding: 'utf-8', timeout: 60000 }
202
+ ).trim()
203
+ try { await fs.unlink(tmpPrompt) } catch (e) {}
204
+
205
+ const headerIndex = changelogEntry.indexOf('### v')
206
+ if (headerIndex > 0) {
207
+ changelogEntry = changelogEntry.substring(headerIndex)
208
+ }
209
+
210
+ const lines = changelogEntry.split('\n')
211
+ const lastBulletIndex = lines.reduce((last, line, i) => line.startsWith('* ') ? i : last, -1)
212
+ if (lastBulletIndex >= 0) {
213
+ changelogEntry = lines.slice(0, lastBulletIndex + 1).join('\n')
214
+ }
215
+
216
+ console.log('Claude generated changelog:\n' + changelogEntry)
217
+ } catch (e) {
218
+ console.error('Claude changelog generation failed, using fallback:', e.message)
219
+ changelogEntry = `### v${version}\nReleased on ${today}\n* CHORE: Release v${version}.`
220
+ }
221
+
222
+ let changelog = await fs.readFile(changelogPath, 'utf-8')
223
+ const headerEnd = '[//]: #@corifeus-header:end'
224
+ const headerEndIndex = changelog.indexOf(headerEnd)
225
+ if (headerEndIndex !== -1) {
226
+ const insertPos = headerEndIndex + headerEnd.length
227
+ changelog = changelog.slice(0, insertPos) + '\n\n' + changelogEntry + '\n' + changelog.slice(insertPos).replace(/^\n+/, '\n')
228
+ await fs.writeFile(changelogPath, changelog)
229
+ console.log(`Changelog updated for v${version}`)
230
+ } else {
231
+ console.error('Could not find header end marker in change-log.md')
232
+ }
233
+
234
+ execSync(`git add change-log.md && git commit -m "chore: update changelog for v${version}"`, {
235
+ cwd,
236
+ stdio: 'inherit',
237
+ })
238
+
239
+ return changelogEntry
240
+ }
241
+
242
+ /**
243
+ * Get changelog entry for a specific version
244
+ */
245
+ const getChangelogEntry = (changelogPath, version) => {
246
+ try {
247
+ const changelog = fsSync.readFileSync(changelogPath, 'utf-8')
248
+ const versionEscaped = version.replace(/\./g, '\\.')
249
+ const regex = new RegExp(`### v${versionEscaped}\\n([\\s\\S]*?)(?=\\n### v|\\n\\[//\\]|$)`)
250
+ const match = changelog.match(regex)
251
+ if (match) {
252
+ return `### v${version}\n${match[1].trim()}`
253
+ }
254
+ } catch (e) {}
255
+ return ''
256
+ }
257
+
258
+ module.exports = {
259
+ generateChangelog,
260
+ getChangelogEntry,
261
+ collectLogs,
262
+ getCommitsSinceLastBump,
263
+ }
@@ -0,0 +1,126 @@
1
+ const https = require('https')
2
+ const { getChangelogEntry } = require('./changelog')
3
+
4
+ const githubRequest = (token, method, apiPath, body) => new Promise((resolve, reject) => {
5
+ const options = {
6
+ hostname: 'api.github.com',
7
+ path: apiPath,
8
+ method,
9
+ headers: {
10
+ 'Authorization': `Bearer ${token}`,
11
+ 'Accept': 'application/vnd.github+json',
12
+ 'User-Agent': 'corifeus-builder',
13
+ 'X-GitHub-Api-Version': '2022-11-28',
14
+ },
15
+ }
16
+ if (body) {
17
+ options.headers['Content-Type'] = 'application/json'
18
+ }
19
+ const req = https.request(options, (res) => {
20
+ let data = ''
21
+ res.on('data', chunk => data += chunk)
22
+ res.on('end', () => {
23
+ try { resolve({ status: res.statusCode, data: JSON.parse(data) }) }
24
+ catch { resolve({ status: res.statusCode, data }) }
25
+ })
26
+ })
27
+ req.on('error', reject)
28
+ if (body) req.write(JSON.stringify(body))
29
+ req.end()
30
+ })
31
+
32
+ /**
33
+ * Create or update a GitHub release with changelog.
34
+ *
35
+ * @param {Object} options
36
+ * @param {string} options.token - GitHub token
37
+ * @param {string} options.owner - GitHub owner (e.g. "patrikx3")
38
+ * @param {string} options.repo - GitHub repo name (e.g. "network-mcp")
39
+ * @param {string} options.version - version string (e.g. "2026.4.105")
40
+ * @param {string} options.projectName - display name (e.g. "P3X Network MCP")
41
+ * @param {string} options.changelogPath - path to change-log.md
42
+ * @param {string} [options.extraBody] - extra content to append to release body
43
+ */
44
+ const createOrUpdateRelease = async (options) => {
45
+ const { token, owner, repo, version, projectName, changelogPath, extraBody } = options
46
+ const releaseTag = `v${version}`
47
+
48
+ try {
49
+ const changelogEntry = getChangelogEntry(changelogPath, version)
50
+ const bodyParts = [changelogEntry]
51
+ if (extraBody) bodyParts.push(extraBody)
52
+ const releaseBody = bodyParts.filter(Boolean).join('\n\n')
53
+
54
+ // Check if release exists
55
+ const listRes = await githubRequest(token, 'GET', `/repos/${owner}/${repo}/releases?per_page=10`)
56
+ if (listRes.status === 200) {
57
+ const existing = listRes.data.find(r => r.tag_name === releaseTag)
58
+ if (existing) {
59
+ if (existing.draft) {
60
+ // Promote draft to published
61
+ const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${existing.id}`, {
62
+ tag_name: releaseTag,
63
+ draft: false,
64
+ body: releaseBody,
65
+ name: `${projectName} ${releaseTag}`,
66
+ })
67
+ if (patchRes.status === 200) {
68
+ console.log(`GitHub release ${releaseTag} promoted from draft to published`)
69
+ } else {
70
+ console.error(`Failed to promote release: ${patchRes.status}`, patchRes.data)
71
+ }
72
+ } else {
73
+ // Update existing
74
+ const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${existing.id}`, {
75
+ body: releaseBody,
76
+ name: `${projectName} ${releaseTag}`,
77
+ })
78
+ if (patchRes.status === 200) {
79
+ console.log(`GitHub release ${releaseTag} updated with changelog`)
80
+ } else {
81
+ console.error(`Failed to update release: ${patchRes.status}`, patchRes.data)
82
+ }
83
+ }
84
+ return
85
+ }
86
+
87
+ // Check for untagged draft (electron-builder)
88
+ const untaggedDraft = listRes.data.find(r => r.draft === true && r.tag_name.startsWith('untagged'))
89
+ if (untaggedDraft) {
90
+ const patchRes = await githubRequest(token, 'PATCH', `/repos/${owner}/${repo}/releases/${untaggedDraft.id}`, {
91
+ tag_name: releaseTag,
92
+ draft: false,
93
+ body: releaseBody,
94
+ name: `${projectName} ${releaseTag}`,
95
+ })
96
+ if (patchRes.status === 200) {
97
+ console.log(`GitHub untagged draft promoted to ${releaseTag}`)
98
+ } else {
99
+ console.error(`Failed to promote untagged draft: ${patchRes.status}`, patchRes.data)
100
+ }
101
+ return
102
+ }
103
+ }
104
+
105
+ // Create new release
106
+ const createRes = await githubRequest(token, 'POST', `/repos/${owner}/${repo}/releases`, {
107
+ tag_name: releaseTag,
108
+ name: `${projectName} ${releaseTag}`,
109
+ body: releaseBody,
110
+ draft: false,
111
+ prerelease: false,
112
+ })
113
+ if (createRes.status === 201) {
114
+ console.log(`GitHub release ${releaseTag} created: ${createRes.data.html_url}`)
115
+ } else {
116
+ console.error(`Failed to create release: ${createRes.status}`, createRes.data)
117
+ }
118
+ } catch (e) {
119
+ console.error('Failed to manage GitHub release:', e.message)
120
+ }
121
+ }
122
+
123
+ module.exports = {
124
+ createOrUpdateRelease,
125
+ githubRequest,
126
+ }
@@ -0,0 +1,10 @@
1
+ const { generateChangelog, getChangelogEntry } = require('./changelog')
2
+ const { createOrUpdateRelease, githubRequest } = require('./github-release')
3
+
4
+ // Export as a library (not a grunt task) — tokens must come from secure/ in each project
5
+ module.exports = {
6
+ generateChangelog,
7
+ getChangelogEntry,
8
+ createOrUpdateRelease,
9
+ githubRequest,
10
+ }
package/src/task/index.js CHANGED
@@ -6,4 +6,5 @@ module.exports = {
6
6
  "generate-tasks": require('./generate-tasks'),
7
7
  "raw-npm-angular": require('./raw-npm-angular'),
8
8
  "inject": require('./inject'),
9
+ "changelog": require('./changelog'),
9
10
  }
@@ -84,6 +84,32 @@ module.exports = async (pkgFile) => {
84
84
 
85
85
  const newPkgFile = JSON.stringify(pkg, null, 4);
86
86
  await fs.writeFile(pkgFile, newPkgFile)
87
+
88
+ // Auto-ensure .npmignore protects sensitive directories
89
+ const cwd = require('path').dirname(pkgFile)
90
+ const npmignorePath = require('path').join(cwd, '.npmignore')
91
+ const protectedDirs = ['secure/', 'agents/', '.claude/', '.vscode/', '.codex/']
92
+ const existingDirs = []
93
+ for (const dir of protectedDirs) {
94
+ try {
95
+ await fs.access(require('path').join(cwd, dir.replace('/', '')))
96
+ existingDirs.push(dir)
97
+ } catch (e) {}
98
+ }
99
+ if (existingDirs.length > 0) {
100
+ let npmignore = ''
101
+ try {
102
+ npmignore = await fs.readFile(npmignorePath, 'utf-8')
103
+ } catch (e) {}
104
+ const missing = existingDirs.filter(dir => !npmignore.split('\n').some(line => line.trim() === dir || line.trim() === dir.replace('/', '')))
105
+ if (missing.length > 0) {
106
+ const addition = missing.join('\n')
107
+ npmignore = npmignore ? npmignore.trimEnd() + '\n' + addition + '\n' : addition + '\n'
108
+ await fs.writeFile(npmignorePath, npmignore)
109
+ console.log(`Auto-added to .npmignore: ${missing.join(', ')}`)
110
+ }
111
+ }
112
+
87
113
  return pkg;
88
114
  }
89
115