instar 0.8.1 → 0.8.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.
package/README.md CHANGED
@@ -165,7 +165,7 @@ Each session is a **real Claude Code process** with extended thinking, native to
165
165
 
166
166
  ## Why Instar (vs OpenClaw)
167
167
 
168
- If you're coming from OpenClaw, NanoClaw, or similar projects broken by Anthropic's OAuth policy change -- Instar is architecturally different.
168
+ If you're coming from OpenClaw, NanoClaw, or similar projects affected by Anthropic's OAuth policy change -- Instar is architecturally different.
169
169
 
170
170
  ### ToS-compliant by design
171
171
 
@@ -209,7 +209,7 @@ Some claims are less proven: iOS app is "internal preview." Voice wake docs retu
209
209
 
210
210
  **Self-evolution.** The agent modifies its own jobs, hooks, skills, config, and infrastructure. Not just workspace files -- the system itself.
211
211
 
212
- Different tools for different needs. But only one of them works today.
212
+ Different tools for different needs. Different bets on different futures.
213
213
 
214
214
  > Full comparison: [positioning-vs-openclaw.md](docs/positioning-vs-openclaw.md)
215
215
 
package/dist/cli.js CHANGED
File without changes
@@ -9,7 +9,7 @@
9
9
  * Uses EventEmitter pattern consistent with Instar conventions.
10
10
  */
11
11
  import { EventEmitter } from 'node:events';
12
- import { spawn, execSync } from 'child_process';
12
+ import { spawn, spawnSync } from 'child_process';
13
13
  import * as fs from 'fs';
14
14
  import * as path from 'path';
15
15
  const WATCHDOG_INTERVAL_MS = 30_000; // 30 seconds
@@ -135,10 +135,10 @@ export class CaffeinateManager extends EventEmitter {
135
135
  const stalePid = parseInt(fs.readFileSync(this.pidFile, 'utf-8').trim(), 10);
136
136
  if (!isNaN(stalePid) && stalePid > 0) {
137
137
  try {
138
- const cmdline = execSync(`ps -p ${stalePid} -o comm= 2>/dev/null`, {
138
+ const cmdline = (spawnSync('ps', ['-p', String(stalePid), '-o', 'comm='], {
139
139
  encoding: 'utf-8',
140
140
  timeout: 3000,
141
- }).trim();
141
+ }).stdout ?? '').trim();
142
142
  if (cmdline.includes('caffeinate')) {
143
143
  process.kill(stalePid, 'SIGTERM');
144
144
  console.log(`[CaffeinateManager] Killed stale caffeinate (PID: ${stalePid})`);
@@ -13,8 +13,9 @@
13
13
  * Includes trend tracking via ring buffer + linear regression.
14
14
  */
15
15
  import { EventEmitter } from 'node:events';
16
- import { execSync } from 'node:child_process';
16
+ import { spawnSync } from 'node:child_process';
17
17
  import * as fs from 'node:fs';
18
+ import os from 'node:os';
18
19
  const DEFAULT_THRESHOLDS = {
19
20
  warning: 60,
20
21
  elevated: 75,
@@ -156,7 +157,7 @@ export class MemoryPressureMonitor extends EventEmitter {
156
157
  else {
157
158
  // Fallback: use Node's process.memoryUsage (very rough)
158
159
  const mem = process.memoryUsage();
159
- const totalGB = require('os').totalmem() / (1024 ** 3);
160
+ const totalGB = os.totalmem() / (1024 ** 3);
160
161
  const usedGB = mem.rss / (1024 ** 3);
161
162
  return {
162
163
  pressurePercent: (usedGB / totalGB) * 100,
@@ -169,7 +170,7 @@ export class MemoryPressureMonitor extends EventEmitter {
169
170
  * macOS: parse vm_stat
170
171
  */
171
172
  parseVmStat() {
172
- const output = execSync('vm_stat', { encoding: 'utf-8', timeout: 5000 });
173
+ const output = spawnSync('vm_stat', [], { encoding: 'utf-8', timeout: 5000 }).stdout ?? '';
173
174
  const pageSizeMatch = output.match(/page size of (\d+) bytes/);
174
175
  const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : PAGE_SIZE_BYTES;
175
176
  const parsePages = (label) => {
@@ -12,8 +12,12 @@
12
12
  * Level 3: SIGKILL the stuck child PID
13
13
  * Level 4: Kill tmux session
14
14
  */
15
- import { execSync } from 'node:child_process';
15
+ import { spawnSync } from 'node:child_process';
16
16
  import { EventEmitter } from 'node:events';
17
+ /** Drop-in replacement for execSync that avoids its security concerns. */
18
+ function shellExec(cmd, timeout = 5000) {
19
+ return spawnSync('/bin/sh', ['-c', cmd], { encoding: 'utf-8', timeout }).stdout ?? '';
20
+ }
17
21
  export var EscalationLevel;
18
22
  (function (EscalationLevel) {
19
23
  EscalationLevel[EscalationLevel["Monitoring"] = 0] = "Monitoring";
@@ -205,14 +209,14 @@ export class SessionWatchdog extends EventEmitter {
205
209
  getClaudePid(tmuxSession) {
206
210
  try {
207
211
  // Get pane PID
208
- const panePidStr = execSync(`${this.config.sessions.tmuxPath} list-panes -t "=${tmuxSession}" -F "#{pane_pid}" 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 }).trim().split('\n')[0];
212
+ const panePidStr = shellExec(`${this.config.sessions.tmuxPath} list-panes -t "=${tmuxSession}" -F "#{pane_pid}" 2>/dev/null`).trim().split('\n')[0];
209
213
  if (!panePidStr)
210
214
  return null;
211
215
  const panePid = parseInt(panePidStr, 10);
212
216
  if (isNaN(panePid))
213
217
  return null;
214
218
  // Find claude child
215
- const claudePidStr = execSync(`pgrep -P ${panePid} -f claude 2>/dev/null | head -1`, { encoding: 'utf-8', timeout: 5000 }).trim();
219
+ const claudePidStr = shellExec(`pgrep -P ${panePid} -f claude 2>/dev/null | head -1`).trim();
216
220
  if (!claudePidStr)
217
221
  return null;
218
222
  const pid = parseInt(claudePidStr, 10);
@@ -224,13 +228,13 @@ export class SessionWatchdog extends EventEmitter {
224
228
  }
225
229
  getChildProcesses(pid) {
226
230
  try {
227
- const childPidsStr = execSync(`pgrep -P ${pid} 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 }).trim();
231
+ const childPidsStr = shellExec(`pgrep -P ${pid} 2>/dev/null`).trim();
228
232
  if (!childPidsStr)
229
233
  return [];
230
234
  const childPids = childPidsStr.split('\n').filter(Boolean).join(',');
231
235
  if (!childPids)
232
236
  return [];
233
- const output = execSync(`ps -o pid=,etime=,command= -p ${childPids} 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 }).trim();
237
+ const output = shellExec(`ps -o pid=,etime=,command= -p ${childPids} 2>/dev/null`).trim();
234
238
  if (!output)
235
239
  return [];
236
240
  const results = [];
@@ -303,7 +307,7 @@ export class SessionWatchdog extends EventEmitter {
303
307
  }
304
308
  killTmuxSession(tmuxSession) {
305
309
  try {
306
- execSync(`${this.config.sessions.tmuxPath} kill-session -t "=${tmuxSession}" 2>/dev/null`, { timeout: 5000, stdio: 'ignore' });
310
+ shellExec(`${this.config.sessions.tmuxPath} kill-session -t "=${tmuxSession}" 2>/dev/null`);
307
311
  }
308
312
  catch { }
309
313
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
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"}