kanon-cli 0.1.2 → 0.1.3

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/bin/kanon.js CHANGED
@@ -22,7 +22,7 @@ const program = new Command();
22
22
  program
23
23
  .name('kanon')
24
24
  .description('Kanon board CLI')
25
- .version('0.1.2');
25
+ .version('0.1.3');
26
26
 
27
27
  // Default action: launch dashboard when no command is given
28
28
  program.action(async (options, cmd) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kanon-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Kanon board CLI — watch boards, spawn Claude agents, manage cards",
5
5
  "type": "module",
6
6
  "bin": {
@@ -116,5 +116,99 @@ export function createDashboardServer(port = 3737) {
116
116
  console.log(`Dashboard server running on http://localhost:${port}`);
117
117
  });
118
118
 
119
+ // --- Nightly auto-update (4:00 AM) ---
120
+ // Checks every 15 min; when hour=4, checks for update, waits for idle workers, then updates & restarts.
121
+ let autoUpdateDone = null; // date string of last auto-update to avoid repeating same night
122
+
123
+ const autoUpdateTimer = setInterval(async () => {
124
+ try {
125
+ const now = new Date();
126
+ // Use local date string to match local getHours() check
127
+ const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
128
+ if (now.getHours() !== 4 || autoUpdateDone === today) return;
129
+
130
+ // Check for update
131
+ const pkgPath = path.resolve(__dirname, '../../../package.json');
132
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
133
+ const current = pkg.version;
134
+
135
+ const resp = await fetch('https://registry.npmjs.org/kanon-cli/latest', {
136
+ headers: { 'Accept': 'application/json' },
137
+ signal: AbortSignal.timeout(5000),
138
+ });
139
+ if (!resp.ok) return;
140
+ const { version: latest } = await resp.json();
141
+ if (!latest || latest === current) {
142
+ autoUpdateDone = today;
143
+ return;
144
+ }
145
+
146
+ // Check if workers are idle (active or queued)
147
+ let busy = false;
148
+ try {
149
+ const lockPath = getAgentLockPath();
150
+ if (fs.existsSync(lockPath)) {
151
+ const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
152
+ // Only query daemon if its process is alive
153
+ let alive = false;
154
+ try { process.kill(lock.pid, 0); alive = true; } catch {}
155
+ if (alive) {
156
+ const statusRes = await fetch(`http://127.0.0.1:${lock.port}/status`, {
157
+ signal: AbortSignal.timeout(3000),
158
+ });
159
+ if (statusRes.ok) {
160
+ const status = await statusRes.json();
161
+ busy = (status.activeWorkers || 0) > 0 || (status.queueLength || 0) > 0;
162
+ }
163
+ }
164
+ }
165
+ } catch {}
166
+
167
+ if (busy) {
168
+ // Workers or queued tasks — try again next interval
169
+ console.log(`[auto-update] Update ${current} → ${latest} available, but workers/queue active. Retrying later.`);
170
+ return;
171
+ }
172
+
173
+ // All clear — install update
174
+ console.log(`[auto-update] Updating kanon-cli ${current} → ${latest}...`);
175
+ autoUpdateDone = today;
176
+
177
+ try {
178
+ execSync('npm install -g kanon-cli@latest', { stdio: 'pipe', timeout: 60000 });
179
+ console.log(`[auto-update] Updated to ${latest}. Restarting...`);
180
+ } catch (err) {
181
+ console.error('[auto-update] npm install failed:', err.message);
182
+ return;
183
+ }
184
+
185
+ // Stop watch daemon before restart
186
+ try {
187
+ const lockPath = getAgentLockPath();
188
+ if (fs.existsSync(lockPath)) {
189
+ const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
190
+ process.kill(lock.pid, 'SIGTERM');
191
+ }
192
+ } catch {}
193
+
194
+ // Restart dashboard
195
+ setTimeout(() => {
196
+ const binPath = path.resolve(__dirname, '../../../bin/kanon.js');
197
+ const child = spawn(process.execPath, [binPath, 'dashboard', '--no-browser', '-p', String(port)], {
198
+ stdio: 'ignore',
199
+ detached: true,
200
+ cwd: process.cwd(),
201
+ });
202
+ child.unref();
203
+ process.exit(0);
204
+ }, 1000);
205
+ } catch (err) {
206
+ console.error('[auto-update] Error:', err.message);
207
+ }
208
+ }, 15 * 60 * 1000); // check every 15 minutes
209
+
210
+ // Cleanup on server close
211
+ server.on('close', () => clearInterval(autoUpdateTimer));
212
+
119
213
  return server;
120
214
  }