@luckydraw/cumulus 0.28.7 → 0.28.9

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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Voice TTS module — manages Piper subprocess for text-to-speech.
3
+ *
4
+ * Sentence buffering: accumulates tokens until sentence boundary,
5
+ * then pipes each sentence to Piper and streams raw PCM back.
6
+ *
7
+ * Audio format: 16-bit signed PCM, 22050 Hz, mono.
8
+ */
9
+ export declare const SAMPLE_RATE = 22050;
10
+ export declare const CHANNELS = 1;
11
+ export declare const BIT_DEPTH = 16;
12
+ /**
13
+ * Buffers streaming tokens into complete sentences for TTS.
14
+ */
15
+ export declare class SentenceBuffer {
16
+ private buffer;
17
+ private onSentence;
18
+ constructor(onSentence: (sentence: string) => void);
19
+ /** Add a token to the buffer. Flushes complete sentences. */
20
+ push(token: string): void;
21
+ /** Flush any remaining text in the buffer. */
22
+ flush(): void;
23
+ /** Clear the buffer without flushing. */
24
+ clear(): void;
25
+ }
26
+ /**
27
+ * Check if Piper and the specified voice are available.
28
+ */
29
+ export declare function isPiperAvailable(voice?: string): boolean;
30
+ export interface VoiceTTSSessionOptions {
31
+ voice?: string;
32
+ /** Called with raw PCM chunks (16-bit signed, 22050 Hz, mono) */
33
+ onAudio: (pcm: Buffer) => void;
34
+ /** Called when all queued sentences have been spoken */
35
+ onDone: () => void;
36
+ /** Called on error */
37
+ onError?: (err: Error) => void;
38
+ }
39
+ /**
40
+ * Manages a voice TTS session — buffers tokens into sentences,
41
+ * synthesizes each with Piper, and streams PCM audio back.
42
+ */
43
+ export declare class VoiceTTSSession {
44
+ private voice;
45
+ private sentenceBuffer;
46
+ private queue;
47
+ private processing;
48
+ private aborted;
49
+ private finished;
50
+ private onAudio;
51
+ private onDone;
52
+ private onError;
53
+ private activeProc;
54
+ constructor(opts: VoiceTTSSessionOptions);
55
+ /** Feed a streaming token into the session. */
56
+ pushToken(token: string): void;
57
+ /** Signal that no more tokens will arrive. Flushes remaining buffer. */
58
+ finish(): void;
59
+ /** Abort the session — stop all synthesis immediately. */
60
+ abort(): void;
61
+ private processQueue;
62
+ private synthesizeSentence;
63
+ }
64
+ //# sourceMappingURL=voice-tts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-tts.d.ts","sourceRoot":"","sources":["../../src/gateway/voice-tts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,eAAO,MAAM,WAAW,QAAQ,CAAC;AACjC,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,SAAS,KAAK,CAAC;AAM5B;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,UAAU,CAA6B;gBAEnC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI;IAIlD,6DAA6D;IAC7D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IA0BzB,8CAA8C;IAC9C,KAAK,IAAI,IAAI;IAQb,yCAAyC;IACzC,KAAK,IAAI,IAAI;CAGd;AAWD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAgB,GAAG,OAAO,CAG/D;AAID,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,wDAAwD;IACxD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,sBAAsB;IACtB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAA6B;gBAEnC,IAAI,EAAE,sBAAsB;IAYxC,+CAA+C;IAC/C,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAc9B,wEAAwE;IACxE,MAAM,IAAI,IAAI;IAUd,0DAA0D;IAC1D,KAAK,IAAI,IAAI;YAUC,YAAY;IAuB1B,OAAO,CAAC,kBAAkB;CAmC3B"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Voice TTS module — manages Piper subprocess for text-to-speech.
3
+ *
4
+ * Sentence buffering: accumulates tokens until sentence boundary,
5
+ * then pipes each sentence to Piper and streams raw PCM back.
6
+ *
7
+ * Audio format: 16-bit signed PCM, 22050 Hz, mono.
8
+ */
9
+ import { spawn } from 'child_process';
10
+ import * as fs from 'fs';
11
+ import * as os from 'os';
12
+ import * as path from 'path';
13
+ // ─── Configuration ───────────────────────────────────────────────────────────
14
+ const PIPER_VOICES_DIR = path.join(os.homedir(), '.cumulus', 'piper-voices');
15
+ const DEFAULT_VOICE = 'en_US-amy-medium';
16
+ export const SAMPLE_RATE = 22050;
17
+ export const CHANNELS = 1;
18
+ export const BIT_DEPTH = 16;
19
+ // ─── Sentence buffering ─────────────────────────────────────────────────────
20
+ const SENTENCE_ENDINGS = /[.!?]\s|[.!?]$/;
21
+ /**
22
+ * Buffers streaming tokens into complete sentences for TTS.
23
+ */
24
+ export class SentenceBuffer {
25
+ buffer = '';
26
+ onSentence;
27
+ constructor(onSentence) {
28
+ this.onSentence = onSentence;
29
+ }
30
+ /** Add a token to the buffer. Flushes complete sentences. */
31
+ push(token) {
32
+ this.buffer += token;
33
+ // Check for sentence boundaries
34
+ let match;
35
+ while ((match = SENTENCE_ENDINGS.exec(this.buffer)) !== null) {
36
+ const endIdx = match.index + match[0].length;
37
+ const sentence = this.buffer.slice(0, endIdx).trim();
38
+ this.buffer = this.buffer.slice(endIdx);
39
+ if (sentence) {
40
+ this.onSentence(sentence);
41
+ }
42
+ }
43
+ // Also check for paragraph breaks
44
+ const paraIdx = this.buffer.indexOf('\n\n');
45
+ if (paraIdx >= 0) {
46
+ const sentence = this.buffer.slice(0, paraIdx).trim();
47
+ this.buffer = this.buffer.slice(paraIdx + 2);
48
+ if (sentence) {
49
+ this.onSentence(sentence);
50
+ }
51
+ }
52
+ }
53
+ /** Flush any remaining text in the buffer. */
54
+ flush() {
55
+ const remaining = this.buffer.trim();
56
+ this.buffer = '';
57
+ if (remaining) {
58
+ this.onSentence(remaining);
59
+ }
60
+ }
61
+ /** Clear the buffer without flushing. */
62
+ clear() {
63
+ this.buffer = '';
64
+ }
65
+ }
66
+ // ─── Piper subprocess manager ────────────────────────────────────────────────
67
+ /**
68
+ * Resolves the path to a Piper voice model.
69
+ */
70
+ function getVoicePath(voice) {
71
+ return path.join(PIPER_VOICES_DIR, `${voice}.onnx`);
72
+ }
73
+ /**
74
+ * Check if Piper and the specified voice are available.
75
+ */
76
+ export function isPiperAvailable(voice = DEFAULT_VOICE) {
77
+ const voicePath = getVoicePath(voice);
78
+ return fs.existsSync(voicePath);
79
+ }
80
+ /**
81
+ * Manages a voice TTS session — buffers tokens into sentences,
82
+ * synthesizes each with Piper, and streams PCM audio back.
83
+ */
84
+ export class VoiceTTSSession {
85
+ voice;
86
+ sentenceBuffer;
87
+ queue = [];
88
+ processing = false;
89
+ aborted = false;
90
+ finished = false;
91
+ onAudio;
92
+ onDone;
93
+ onError;
94
+ activeProc = null;
95
+ constructor(opts) {
96
+ this.voice = opts.voice || DEFAULT_VOICE;
97
+ this.onAudio = opts.onAudio;
98
+ this.onDone = opts.onDone;
99
+ this.onError = opts.onError || (() => { });
100
+ this.sentenceBuffer = new SentenceBuffer(sentence => {
101
+ this.queue.push(sentence);
102
+ this.processQueue();
103
+ });
104
+ }
105
+ /** Feed a streaming token into the session. */
106
+ pushToken(token) {
107
+ if (this.aborted)
108
+ return;
109
+ // Strip markdown/blex for TTS
110
+ const clean = token
111
+ .replace(/~~~blex:[\s\S]*?~~~/g, '')
112
+ .replace(/```[\s\S]*?```/g, '')
113
+ .replace(/[*_~`#]/g, '');
114
+ if (clean) {
115
+ this.sentenceBuffer.push(clean);
116
+ }
117
+ }
118
+ /** Signal that no more tokens will arrive. Flushes remaining buffer. */
119
+ finish() {
120
+ if (this.aborted)
121
+ return;
122
+ this.finished = true;
123
+ this.sentenceBuffer.flush();
124
+ // If queue is empty and not processing, we're done
125
+ if (this.queue.length === 0 && !this.processing) {
126
+ this.onDone();
127
+ }
128
+ }
129
+ /** Abort the session — stop all synthesis immediately. */
130
+ abort() {
131
+ this.aborted = true;
132
+ this.queue.length = 0;
133
+ this.sentenceBuffer.clear();
134
+ if (this.activeProc) {
135
+ this.activeProc.kill('SIGTERM');
136
+ this.activeProc = null;
137
+ }
138
+ }
139
+ async processQueue() {
140
+ if (this.processing || this.aborted)
141
+ return;
142
+ this.processing = true;
143
+ while (this.queue.length > 0 && !this.aborted) {
144
+ const sentence = this.queue.shift();
145
+ try {
146
+ await this.synthesizeSentence(sentence);
147
+ }
148
+ catch (err) {
149
+ if (!this.aborted) {
150
+ this.onError(err instanceof Error ? err : new Error(String(err)));
151
+ }
152
+ }
153
+ }
154
+ this.processing = false;
155
+ // If finished and queue is drained, signal done
156
+ if (this.finished && this.queue.length === 0 && !this.aborted) {
157
+ this.onDone();
158
+ }
159
+ }
160
+ synthesizeSentence(text) {
161
+ return new Promise((resolve, reject) => {
162
+ const voicePath = getVoicePath(this.voice);
163
+ const proc = spawn('python3', ['-m', 'piper', '-m', voicePath, '--output-raw'], {
164
+ stdio: ['pipe', 'pipe', 'pipe'],
165
+ });
166
+ this.activeProc = proc;
167
+ proc.stdout.on('data', (chunk) => {
168
+ // Stream audio chunks directly to the client as they arrive
169
+ if (!this.aborted) {
170
+ this.onAudio(chunk);
171
+ }
172
+ });
173
+ proc.on('error', err => {
174
+ this.activeProc = null;
175
+ reject(err);
176
+ });
177
+ proc.on('close', code => {
178
+ this.activeProc = null;
179
+ if (code !== 0) {
180
+ reject(new Error(`Piper exited with code ${code}`));
181
+ }
182
+ else {
183
+ resolve();
184
+ }
185
+ });
186
+ proc.stdin.write(text);
187
+ proc.stdin.end();
188
+ });
189
+ }
190
+ }
191
+ //# sourceMappingURL=voice-tts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-tts.js","sourceRoot":"","sources":["../../src/gateway/voice-tts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Version check module — checks npm registry for newer published versions.
3
+ * Non-blocking, never throws. Caches results for 1 hour.
4
+ */
5
+ export interface VersionInfo {
6
+ current: string;
7
+ latest: string;
8
+ updateAvailable: boolean;
9
+ releaseNotes?: string;
10
+ }
11
+ export interface UpdateState {
12
+ previousVersion: string;
13
+ updatedAt: string;
14
+ updatedFrom: string;
15
+ updatedTo: string;
16
+ }
17
+ /** Get the current installed version from package.json */
18
+ export declare function getCurrentVersion(): string;
19
+ /** Simple semver compare: returns true if latest > current */
20
+ export declare function isNewer(latest: string, current: string): boolean;
21
+ /** Count minor version gap (e.g., 0.28.7 → 0.30.0 = 2) */
22
+ export declare function minorVersionGap(latest: string, current: string): number;
23
+ /**
24
+ * Check for available updates. Caches result for 1 hour.
25
+ * Never throws — returns { updateAvailable: false } on any error.
26
+ */
27
+ export declare function checkForUpdate(skipCache?: boolean): Promise<VersionInfo>;
28
+ /** Clear the cache (useful after an update) */
29
+ export declare function clearVersionCache(): void;
30
+ /** Save the current version before updating (for rollback) */
31
+ export declare function saveUpdateState(from: string, to: string): void;
32
+ /** Read the saved update state (for rollback) */
33
+ export declare function readUpdateState(): UpdateState | null;
34
+ /** Check if sudo is needed for global npm install */
35
+ export declare function needsSudo(): Promise<boolean>;
36
+ /** Run the npm install to update to a specific version */
37
+ export declare function performUpdate(version: string): Promise<{
38
+ success: boolean;
39
+ error?: string;
40
+ }>;
41
+ //# sourceMappingURL=version-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../src/lib/version-check.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,0DAA0D;AAC1D,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AA+BD,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAOhE;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,SAAS,UAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAwB5E;AAED,+CAA+C;AAC/C,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAoCD,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAS9D;AAED,iDAAiD;AACjD,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAOpD;AAID,qDAAqD;AACrD,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CASlD;AAED,0DAA0D;AAC1D,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAc/C"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Version check module — checks npm registry for newer published versions.
3
+ * Non-blocking, never throws. Caches results for 1 hour.
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as https from 'https';
7
+ import { createRequire } from 'module';
8
+ import * as os from 'os';
9
+ import * as path from 'path';
10
+ const _require = createRequire(import.meta.url);
11
+ const PACKAGE_NAME = '@luckydraw/cumulus';
12
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
13
+ const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
14
+ const UPDATE_STATE_FILE = path.join(os.homedir(), '.cumulus', 'update-state.json');
15
+ let cachedResult = null;
16
+ let cacheTimestamp = 0;
17
+ /** Get the current installed version from package.json */
18
+ export function getCurrentVersion() {
19
+ return _require('../../package.json').version;
20
+ }
21
+ /** Fetch latest version from npm registry */
22
+ function fetchLatestVersion() {
23
+ return new Promise((resolve, reject) => {
24
+ const req = https.get(REGISTRY_URL, { headers: { Accept: 'application/json' } }, res => {
25
+ let data = '';
26
+ res.on('data', (chunk) => {
27
+ data += chunk.toString();
28
+ });
29
+ res.on('end', () => {
30
+ try {
31
+ const json = JSON.parse(data);
32
+ if (json.version) {
33
+ resolve(json.version);
34
+ }
35
+ else {
36
+ reject(new Error('No version field in registry response'));
37
+ }
38
+ }
39
+ catch (e) {
40
+ reject(e);
41
+ }
42
+ });
43
+ });
44
+ req.on('error', reject);
45
+ req.setTimeout(10000, () => {
46
+ req.destroy();
47
+ reject(new Error('Registry request timed out'));
48
+ });
49
+ });
50
+ }
51
+ /** Simple semver compare: returns true if latest > current */
52
+ export function isNewer(latest, current) {
53
+ const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
54
+ const [lMaj = 0, lMin = 0, lPat = 0] = parse(latest);
55
+ const [cMaj = 0, cMin = 0, cPat = 0] = parse(current);
56
+ if (lMaj !== cMaj)
57
+ return lMaj > cMaj;
58
+ if (lMin !== cMin)
59
+ return lMin > cMin;
60
+ return lPat > cPat;
61
+ }
62
+ /** Count minor version gap (e.g., 0.28.7 → 0.30.0 = 2) */
63
+ export function minorVersionGap(latest, current) {
64
+ const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
65
+ const [lMaj = 0, lMin = 0] = parse(latest);
66
+ const [cMaj = 0, cMin = 0] = parse(current);
67
+ if (lMaj !== cMaj)
68
+ return 99; // major version gap
69
+ return lMin - cMin;
70
+ }
71
+ /**
72
+ * Check for available updates. Caches result for 1 hour.
73
+ * Never throws — returns { updateAvailable: false } on any error.
74
+ */
75
+ export async function checkForUpdate(skipCache = false) {
76
+ const now = Date.now();
77
+ if (!skipCache && cachedResult && now - cacheTimestamp < CACHE_TTL_MS) {
78
+ return cachedResult;
79
+ }
80
+ const current = getCurrentVersion();
81
+ try {
82
+ const latest = await fetchLatestVersion();
83
+ const updateAvailable = isNewer(latest, current);
84
+ const result = { current, latest, updateAvailable };
85
+ // Try to read release notes from local CHANGELOG.md
86
+ if (updateAvailable) {
87
+ result.releaseNotes = readChangelogNotes(current, latest);
88
+ }
89
+ cachedResult = result;
90
+ cacheTimestamp = now;
91
+ return result;
92
+ }
93
+ catch {
94
+ return { current, latest: current, updateAvailable: false };
95
+ }
96
+ }
97
+ /** Clear the cache (useful after an update) */
98
+ export function clearVersionCache() {
99
+ cachedResult = null;
100
+ cacheTimestamp = 0;
101
+ }
102
+ /** Read release notes from CHANGELOG.md between two versions */
103
+ function readChangelogNotes(fromVersion, toVersion) {
104
+ try {
105
+ const changelogPath = path.resolve(_require.resolve('../../package.json'), '..', 'CHANGELOG.md');
106
+ const content = fs.readFileSync(changelogPath, 'utf-8');
107
+ // Extract sections between fromVersion and toVersion
108
+ const lines = content.split('\n');
109
+ const notes = [];
110
+ let capturing = false;
111
+ for (const line of lines) {
112
+ const versionMatch = line.match(/^## v?(\d+\.\d+\.\d+)/);
113
+ if (versionMatch) {
114
+ const v = versionMatch[1];
115
+ if (v === fromVersion || (!isNewer(v, fromVersion) && v !== toVersion)) {
116
+ break; // Stop at or before the current version
117
+ }
118
+ capturing = true;
119
+ }
120
+ if (capturing) {
121
+ notes.push(line);
122
+ }
123
+ }
124
+ return notes.length > 0 ? notes.join('\n').trim() : undefined;
125
+ }
126
+ catch {
127
+ return undefined;
128
+ }
129
+ }
130
+ // ─── Update state (for rollback) ─────────────────────────────
131
+ /** Save the current version before updating (for rollback) */
132
+ export function saveUpdateState(from, to) {
133
+ const state = {
134
+ previousVersion: from,
135
+ updatedAt: new Date().toISOString(),
136
+ updatedFrom: from,
137
+ updatedTo: to,
138
+ };
139
+ fs.mkdirSync(path.dirname(UPDATE_STATE_FILE), { recursive: true });
140
+ fs.writeFileSync(UPDATE_STATE_FILE, JSON.stringify(state, null, 2), 'utf-8');
141
+ }
142
+ /** Read the saved update state (for rollback) */
143
+ export function readUpdateState() {
144
+ try {
145
+ const raw = fs.readFileSync(UPDATE_STATE_FILE, 'utf-8');
146
+ return JSON.parse(raw);
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ // ─── Perform update ──────────────────────────────────────────
153
+ /** Check if sudo is needed for global npm install */
154
+ export async function needsSudo() {
155
+ const { execSync } = await import('child_process');
156
+ try {
157
+ const prefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim();
158
+ fs.accessSync(prefix, fs.constants.W_OK);
159
+ return false;
160
+ }
161
+ catch {
162
+ return true;
163
+ }
164
+ }
165
+ /** Run the npm install to update to a specific version */
166
+ export async function performUpdate(version) {
167
+ const { execSync } = await import('child_process');
168
+ const current = getCurrentVersion();
169
+ const sudo = await needsSudo();
170
+ const cmd = `${sudo ? 'sudo ' : ''}npm install -g ${PACKAGE_NAME}@${version}`;
171
+ try {
172
+ saveUpdateState(current, version);
173
+ execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', timeout: 120000 });
174
+ clearVersionCache();
175
+ return { success: true };
176
+ }
177
+ catch (err) {
178
+ return { success: false, error: String(err) };
179
+ }
180
+ }
181
+ //# sourceMappingURL=version-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/lib/version-check.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckydraw/cumulus",
3
- "version": "0.28.7",
3
+ "version": "0.28.9",
4
4
  "description": "RLM-based CLI chat wrapper for Claude with external history context management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,8 @@
24
24
  },
25
25
  "files": [
26
26
  "dist/",
27
- "package.json"
27
+ "package.json",
28
+ "CHANGELOG.md"
28
29
  ],
29
30
  "bin": {
30
31
  "cumulus": "./dist/cli/cumulus.js",
@@ -40,6 +41,7 @@
40
41
  "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
41
42
  "type-check": "tsc --noEmit",
42
43
  "test": "vitest",
44
+ "preversion": "bash scripts/generate-changelog.sh || true",
43
45
  "prepublishOnly": "npm run build && npm run type-check",
44
46
  "prepare": "husky"
45
47
  },