instar 0.7.50 → 0.7.52

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.
@@ -54,11 +54,17 @@ export class AutoUpdater {
54
54
  if (this.interval)
55
55
  return;
56
56
  const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
57
- // Warn if running from npx cache (auto-updates won't work properly)
57
+ // Detect npx cache auto-apply and restart cause infinite loops when
58
+ // running from npx because the cache still resolves to the old version
59
+ // after npm installs the update. The restart finds the update again,
60
+ // applies it again, restarts again — forever, killing all sessions each time.
58
61
  const scriptPath = process.argv[1] || '';
59
- if (scriptPath.includes('.npm/_npx') || scriptPath.includes('/_npx/')) {
60
- console.warn('[AutoUpdater] WARNING: Running from npx cache. Auto-updates require a global install.\n' +
61
- '[AutoUpdater] Run: npm install -g instar');
62
+ const runningFromNpx = scriptPath.includes('.npm/_npx') || scriptPath.includes('/_npx/');
63
+ if (runningFromNpx) {
64
+ this.config.autoApply = false;
65
+ this.config.autoRestart = false;
66
+ console.warn('[AutoUpdater] Running from npx cache. Auto-apply and auto-restart disabled to prevent restart loops.\n' +
67
+ '[AutoUpdater] Run: npm install -g instar (then restart with: instar server start)');
62
68
  }
63
69
  console.log(`[AutoUpdater] Started (every ${this.config.checkIntervalMinutes}m, ` +
64
70
  `autoApply: ${this.config.autoApply}, autoRestart: ${this.config.autoRestart})`);
@@ -122,8 +128,7 @@ export class AutoUpdater {
122
128
  if (!this.config.autoApply) {
123
129
  // Just notify — don't apply
124
130
  await this.notify(`Update available: v${info.currentVersion} → v${info.latestVersion}\n\n` +
125
- (info.changeSummary ? `What changed:\n${info.changeSummary}\n\n` : '') +
126
- `Details: ${info.changelogUrl || 'https://github.com/SageMindAI/instar/releases'}\n\n` +
131
+ (info.changeSummary ? `Changes: ${info.changeSummary}\n\n` : '') +
127
132
  `Auto-apply is disabled. Apply manually:\n` +
128
133
  `curl -X POST http://localhost:${this.getPort()}/updates/apply`);
129
134
  return;
@@ -148,19 +153,13 @@ export class AutoUpdater {
148
153
  console.log(`[AutoUpdater] Updated: v${result.previousVersion} → v${result.newVersion}`);
149
154
  // Step 5: Notify via Telegram
150
155
  const restartNote = result.restartNeeded && this.config.autoRestart
151
- ? '\nServer is restarting now...'
156
+ ? 'Server is restarting now...'
152
157
  : result.restartNeeded
153
- ? '\nA server restart is needed to use the new version.'
158
+ ? 'A server restart is needed to use the new version.'
154
159
  : '';
155
- const changeSummary = info.changeSummary
156
- ? `What changed:\n${info.changeSummary}\n`
157
- : '';
158
- const detailsUrl = info.changelogUrl || 'https://github.com/SageMindAI/instar/releases';
159
160
  await this.notify(`Updated: v${result.previousVersion} → v${result.newVersion}\n\n` +
160
- changeSummary +
161
- `Details: ${detailsUrl}\n` +
162
- restartNote +
163
- `\n\nTo disable auto-updates, set "autoApply": false in .instar/config.json under "updates".`);
161
+ (info.changeSummary ? `What changed:\n${info.changeSummary}\n\n` : '') +
162
+ restartNote);
164
163
  // Step 6: Self-restart if needed and configured
165
164
  if (result.restartNeeded && this.config.autoRestart) {
166
165
  // Brief delay to let the Telegram notification send
@@ -60,8 +60,7 @@ export declare class UpdateChecker {
60
60
  updatedAt: string;
61
61
  } | null;
62
62
  /**
63
- * Fetch human-readable changelog from GitHub releases, falling back to
64
- * recent commit messages if no release exists for this version.
63
+ * Fetch human-readable changelog from GitHub releases.
65
64
  */
66
65
  fetchChangelog(version: string): Promise<string | undefined>;
67
66
  /**
@@ -226,11 +226,9 @@ export class UpdateChecker {
226
226
  }
227
227
  }
228
228
  /**
229
- * Fetch human-readable changelog from GitHub releases, falling back to
230
- * recent commit messages if no release exists for this version.
229
+ * Fetch human-readable changelog from GitHub releases.
231
230
  */
232
231
  async fetchChangelog(version) {
233
- // Try GitHub release first
234
232
  try {
235
233
  const tag = version.startsWith('v') ? version : `v${version}`;
236
234
  const response = await fetch(`${GITHUB_RELEASES_URL}/tags/${tag}`, {
@@ -240,41 +238,16 @@ export class UpdateChecker {
240
238
  },
241
239
  signal: AbortSignal.timeout(10000),
242
240
  });
243
- if (response.ok) {
244
- const release = await response.json();
245
- if (release.body) {
246
- const summary = release.body.slice(0, 500);
247
- return summary.length < release.body.length ? summary + '...' : summary;
248
- }
249
- if (release.name)
250
- return release.name;
251
- }
252
- }
253
- catch {
254
- // Non-critical — try commit fallback
255
- }
256
- // Fallback: fetch recent commits from GitHub
257
- try {
258
- const response = await fetch('https://api.github.com/repos/SageMindAI/instar/commits?per_page=5', {
259
- headers: {
260
- 'Accept': 'application/vnd.github.v3+json',
261
- 'User-Agent': 'instar-update-checker',
262
- },
263
- signal: AbortSignal.timeout(10000),
264
- });
265
- if (response.ok) {
266
- const commits = await response.json();
267
- if (commits.length > 0) {
268
- const lines = commits
269
- .map(c => {
270
- // Take first line of commit message only
271
- const firstLine = c.commit.message.split('\n')[0];
272
- return `• ${firstLine}`;
273
- })
274
- .join('\n');
275
- return `Recent changes:\n${lines}`;
276
- }
241
+ if (!response.ok)
242
+ return undefined;
243
+ const release = await response.json();
244
+ if (release.body) {
245
+ // Truncate to first 500 chars for concise summary
246
+ const summary = release.body.slice(0, 500);
247
+ return summary.length < release.body.length ? summary + '...' : summary;
277
248
  }
249
+ if (release.name)
250
+ return release.name;
278
251
  }
279
252
  catch {
280
253
  // Non-critical
@@ -241,8 +241,23 @@ export class TelegramLifeline {
241
241
  // Forward to server if healthy
242
242
  if (this.supervisor.healthy) {
243
243
  const forwarded = await this.forwardToServer(topicId, text, msg);
244
- if (forwarded)
244
+ if (forwarded) {
245
+ // Delivery confirmation — user knows message reached the server
246
+ await this.sendToTopic(topicId, '✓ Delivered');
245
247
  return;
248
+ }
249
+ // Server appears healthy but forward failed — queue with accurate message
250
+ this.queue.enqueue({
251
+ id: `tg-${msg.message_id}`,
252
+ topicId,
253
+ text,
254
+ fromUserId: msg.from.id,
255
+ fromUsername: msg.from.username,
256
+ fromFirstName: msg.from.first_name,
257
+ timestamp: new Date(msg.date * 1000).toISOString(),
258
+ });
259
+ await this.sendToTopic(topicId, `Server is restarting. Your message has been queued (${this.queue.length} in queue). It will be delivered when the server recovers.`);
260
+ return;
246
261
  }
247
262
  // Server is down — queue the message
248
263
  this.queue.enqueue({
@@ -8,6 +8,7 @@ import { Router } from 'express';
8
8
  import { execFileSync } from 'node:child_process';
9
9
  import { createHash, timingSafeEqual } from 'node:crypto';
10
10
  import fs from 'node:fs';
11
+ import os from 'node:os';
11
12
  import path from 'node:path';
12
13
  import { rateLimiter, signViewPath } from './middleware.js';
13
14
  // Validation patterns for route parameters
@@ -64,7 +65,6 @@ export function createRoutes(ctx) {
64
65
  heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
65
66
  };
66
67
  // System-wide memory state
67
- const os = require('node:os');
68
68
  const totalMem = os.totalmem();
69
69
  const freeMem = os.freemem();
70
70
  base.systemMemory = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.7.50",
3
+ "version": "0.7.52",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,11 +0,0 @@
1
- > Why do I have a folder named ".vercel" in my project?
2
- The ".vercel" folder is created when you link a directory to a Vercel project.
3
-
4
- > What does the "project.json" file contain?
5
- The "project.json" file contains:
6
- - The ID of the Vercel project that you linked ("projectId")
7
- - The ID of the user or team your Vercel project is owned by ("orgId")
8
-
9
- > Should I commit the ".vercel" folder?
10
- No, you should not share the ".vercel" folder with anyone.
11
- Upon creation, it will be automatically added to your ".gitignore" file.
@@ -1 +0,0 @@
1
- {"projectId":"prj_evM5LcItYL3IAmw8zNvEPGrHeaya","orgId":"team_dHctwIDcV3X9ydapQlCPHFGI","projectName":"claude-agent-kit"}