@stackmemoryai/stackmemory 0.5.46 → 0.5.48
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 +26 -40
- package/dist/cli/commands/daemon.js +392 -0
- package/dist/cli/commands/daemon.js.map +7 -0
- package/dist/cli/commands/service.js +55 -1
- package/dist/cli/commands/service.js.map +2 -2
- package/dist/cli/index.js +37 -22
- package/dist/cli/index.js.map +2 -2
- package/dist/core/config/feature-flags.js +7 -1
- package/dist/core/config/feature-flags.js.map +2 -2
- package/dist/daemon/daemon-config.js +149 -0
- package/dist/daemon/daemon-config.js.map +7 -0
- package/dist/daemon/services/context-service.js +122 -0
- package/dist/daemon/services/context-service.js.map +7 -0
- package/dist/daemon/services/linear-service.js +136 -0
- package/dist/daemon/services/linear-service.js.map +7 -0
- package/dist/daemon/unified-daemon.js +276 -0
- package/dist/daemon/unified-daemon.js.map +7 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# StackMemory
|
|
2
2
|
|
|
3
|
-
**Lossless, project-scoped memory for AI tools** • v0.5.
|
|
3
|
+
**Lossless, project-scoped memory for AI tools** • v0.5.47
|
|
4
4
|
|
|
5
5
|
StackMemory is a **production-ready memory runtime** for AI coding tools that preserves full project context across sessions:
|
|
6
6
|
|
|
@@ -30,7 +30,7 @@ StackMemory solves this by:
|
|
|
30
30
|
|
|
31
31
|
- Storing all events, tool calls, and decisions
|
|
32
32
|
- Smart retrieval of relevant context
|
|
33
|
-
- Call stack organization
|
|
33
|
+
- Call stack organization for nested context
|
|
34
34
|
- Configurable importance scoring
|
|
35
35
|
- Team collaboration through shared stacks
|
|
36
36
|
|
|
@@ -92,25 +92,14 @@ The editor never manages memory directly; it asks StackMemory for the **context
|
|
|
92
92
|
|
|
93
93
|
## Product Health Metrics
|
|
94
94
|
|
|
95
|
-
### Current Status (v0.5.
|
|
95
|
+
### Current Status (v0.5.47)
|
|
96
96
|
|
|
97
97
|
| Metric | Current | Target | Status |
|
|
98
98
|
|--------|---------|--------|--------|
|
|
99
99
|
| **Test Coverage** | 85% | 90% | In Progress |
|
|
100
|
-
| **
|
|
101
|
-
| **Documentation** |
|
|
102
|
-
| **Active Issues** |
|
|
103
|
-
| **Code Quality** | 396 tests | 400+ | Done |
|
|
104
|
-
| **npm Downloads** | Growing | 1K+/week | On Track |
|
|
105
|
-
|
|
106
|
-
### Quality Score: 78/100
|
|
107
|
-
|
|
108
|
-
**Formula:** (Test Coverage × 0.3) + (Performance × 0.3) + (Documentation × 0.2) + (Issues Resolution × 0.2)
|
|
109
|
-
|
|
110
|
-
### Next Sprint Priorities
|
|
111
|
-
|
|
112
|
-
1. **[STA-289] Performance Optimization** - Achieve SLA targets
|
|
113
|
-
2. **[STA-291] Code Cleanup** - Zero TODOs, 90% coverage
|
|
100
|
+
| **Tests Passing** | 490 | 500+ | On Track |
|
|
101
|
+
| **Documentation** | 80% | 100% | In Progress |
|
|
102
|
+
| **Active Issues** | 3 high | 0 high | In Progress |
|
|
114
103
|
|
|
115
104
|
---
|
|
116
105
|
|
|
@@ -260,40 +249,33 @@ Available MCP tools in Claude Code:
|
|
|
260
249
|
|
|
261
250
|
## 2. Open-Source Local Mode
|
|
262
251
|
|
|
263
|
-
### Step 1: Clone
|
|
252
|
+
### Step 1: Clone & Build
|
|
264
253
|
|
|
265
254
|
```bash
|
|
266
|
-
git clone https://github.com/
|
|
255
|
+
git clone https://github.com/stackmemoryai/stackmemory
|
|
267
256
|
cd stackmemory
|
|
257
|
+
npm install
|
|
258
|
+
npm run build
|
|
268
259
|
```
|
|
269
260
|
|
|
270
261
|
### Step 2: Run local MCP server
|
|
271
262
|
|
|
272
263
|
```bash
|
|
273
|
-
|
|
274
|
-
# or
|
|
275
|
-
npm run dev
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
This creates:
|
|
279
|
-
|
|
280
|
-
```
|
|
281
|
-
.memory/
|
|
282
|
-
└── memory.db # SQLite
|
|
264
|
+
npm run mcp:start
|
|
265
|
+
# or for development
|
|
266
|
+
npm run mcp:dev
|
|
283
267
|
```
|
|
284
268
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
---
|
|
269
|
+
This creates `.stackmemory/` with SQLite storage.
|
|
288
270
|
|
|
289
271
|
### Step 3: Point your editor to local MCP
|
|
290
272
|
|
|
291
273
|
```json
|
|
292
274
|
{
|
|
293
|
-
"
|
|
275
|
+
"mcpServers": {
|
|
294
276
|
"stackmemory": {
|
|
295
|
-
"command": "
|
|
296
|
-
"args": ["
|
|
277
|
+
"command": "node",
|
|
278
|
+
"args": ["dist/integrations/mcp/server.js"]
|
|
297
279
|
}
|
|
298
280
|
}
|
|
299
281
|
}
|
|
@@ -506,7 +488,7 @@ stackmemory mcp-server [--port 3001]
|
|
|
506
488
|
- Hosted: **Private beta**
|
|
507
489
|
- OSS mirror: **Production ready**
|
|
508
490
|
- MCP integration: **Stable**
|
|
509
|
-
- CLI: **v0.5.
|
|
491
|
+
- CLI: **v0.5.47** - Zero-config setup, diagnostics, full task/context/Linear management
|
|
510
492
|
- Two-tier storage: **Complete**
|
|
511
493
|
- Test Suite: **490 tests passing**
|
|
512
494
|
|
|
@@ -514,13 +496,18 @@ stackmemory mcp-server [--port 3001]
|
|
|
514
496
|
|
|
515
497
|
## Changelog
|
|
516
498
|
|
|
499
|
+
### v0.5.47 (2026-01-27)
|
|
500
|
+
- **Graceful database failures**: Handles native module version mismatches
|
|
501
|
+
- **Suppress dotenv logs**: Cleaner terminal output
|
|
502
|
+
- **TTY preservation**: Fixes interactive mode for claude-sm/claude-smd
|
|
503
|
+
- **Silent Linear auth**: No spam when API key not configured
|
|
504
|
+
|
|
517
505
|
### v0.5.40 (2026-01-27)
|
|
518
506
|
- **Zero-config onboarding**: `stackmemory init` now works without any prompts
|
|
519
507
|
- **New `setup-mcp` command**: Auto-configures Claude Code MCP integration
|
|
520
508
|
- **New `doctor` command**: Diagnoses issues and suggests fixes
|
|
521
509
|
- **Interactive postinstall**: Asks for consent before modifying ~/.claude
|
|
522
510
|
- **Better error messages**: Shows reason + fix + next step
|
|
523
|
-
- 490 tests passing
|
|
524
511
|
|
|
525
512
|
### v0.5.39 (2026-01-27)
|
|
526
513
|
- **AsyncMutex**: Thread-safe Linear sync with stale lock detection
|
|
@@ -567,9 +554,8 @@ stackmemory mcp-server [--port 3001]
|
|
|
567
554
|
## Roadmap
|
|
568
555
|
|
|
569
556
|
**Phase 4 (Completed):** Two-tier storage system with local tiers and infinite remote storage
|
|
570
|
-
**Phase 5 (
|
|
571
|
-
**Phase
|
|
572
|
-
**Phase 4:** Two-tier storage, enterprise features, cost optimization
|
|
557
|
+
**Phase 5 (Current):** PostgreSQL production adapter, enhanced team collaboration, advanced analytics
|
|
558
|
+
**Phase 6 (Next):** Enterprise features, multi-org support, cost optimization
|
|
573
559
|
|
|
574
560
|
---
|
|
575
561
|
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import {
|
|
12
|
+
loadDaemonConfig,
|
|
13
|
+
saveDaemonConfig,
|
|
14
|
+
readDaemonStatus,
|
|
15
|
+
getDaemonPaths,
|
|
16
|
+
DEFAULT_DAEMON_CONFIG
|
|
17
|
+
} from "../../daemon/daemon-config.js";
|
|
18
|
+
function createDaemonCommand() {
|
|
19
|
+
const cmd = new Command("daemon").description("Manage StackMemory unified daemon for background services").addHelpText(
|
|
20
|
+
"after",
|
|
21
|
+
`
|
|
22
|
+
Examples:
|
|
23
|
+
stackmemory daemon start Start the daemon
|
|
24
|
+
stackmemory daemon stop Stop the daemon
|
|
25
|
+
stackmemory daemon status Check daemon status
|
|
26
|
+
stackmemory daemon logs View daemon logs
|
|
27
|
+
stackmemory daemon config Show/edit configuration
|
|
28
|
+
|
|
29
|
+
The daemon provides:
|
|
30
|
+
- Context auto-save (default: every 15 minutes)
|
|
31
|
+
- Linear sync (optional, if configured)
|
|
32
|
+
- File watch (optional, for change detection)
|
|
33
|
+
`
|
|
34
|
+
);
|
|
35
|
+
cmd.command("start").description("Start the unified daemon").option("--foreground", "Run in foreground (for debugging)").option("--save-interval <minutes>", "Context save interval in minutes").option("--linear-interval <minutes>", "Linear sync interval in minutes").option("--no-linear", "Disable Linear sync").option("--log-level <level>", "Log level (debug|info|warn|error)").action(async (options) => {
|
|
36
|
+
const status = readDaemonStatus();
|
|
37
|
+
if (status.running) {
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.yellow("Daemon already running"),
|
|
40
|
+
chalk.gray(`(pid: ${status.pid})`)
|
|
41
|
+
);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const spinner = ora("Starting unified daemon...").start();
|
|
45
|
+
try {
|
|
46
|
+
const args = ["daemon-run"];
|
|
47
|
+
if (options.saveInterval) {
|
|
48
|
+
args.push("--save-interval", options.saveInterval);
|
|
49
|
+
}
|
|
50
|
+
if (options.linearInterval) {
|
|
51
|
+
args.push("--linear-interval", options.linearInterval);
|
|
52
|
+
}
|
|
53
|
+
if (options.linear === false) {
|
|
54
|
+
args.push("--no-linear");
|
|
55
|
+
}
|
|
56
|
+
if (options.logLevel) {
|
|
57
|
+
args.push("--log-level", options.logLevel);
|
|
58
|
+
}
|
|
59
|
+
if (options.foreground) {
|
|
60
|
+
spinner.stop();
|
|
61
|
+
console.log(chalk.cyan("Running in foreground (Ctrl+C to stop)"));
|
|
62
|
+
const { UnifiedDaemon } = await import("../../daemon/unified-daemon.js");
|
|
63
|
+
const config = {};
|
|
64
|
+
if (options.saveInterval) {
|
|
65
|
+
config.context = {
|
|
66
|
+
enabled: true,
|
|
67
|
+
interval: parseInt(options.saveInterval, 10)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (options.linearInterval) {
|
|
71
|
+
config.linear = {
|
|
72
|
+
enabled: true,
|
|
73
|
+
interval: parseInt(options.linearInterval, 10),
|
|
74
|
+
retryAttempts: 3,
|
|
75
|
+
retryDelay: 3e4
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (options.linear === false) {
|
|
79
|
+
config.linear = {
|
|
80
|
+
enabled: false,
|
|
81
|
+
interval: 60,
|
|
82
|
+
retryAttempts: 3,
|
|
83
|
+
retryDelay: 3e4
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const daemon = new UnifiedDaemon(config);
|
|
87
|
+
await daemon.start();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const daemonScript = getDaemonScriptPath();
|
|
91
|
+
if (!daemonScript) {
|
|
92
|
+
spinner.fail(chalk.red("Daemon script not found"));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const daemonProcess = spawn("node", [daemonScript, ...args.slice(1)], {
|
|
96
|
+
detached: true,
|
|
97
|
+
stdio: "ignore",
|
|
98
|
+
env: { ...process.env }
|
|
99
|
+
});
|
|
100
|
+
daemonProcess.unref();
|
|
101
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
102
|
+
const newStatus = readDaemonStatus();
|
|
103
|
+
if (newStatus.running) {
|
|
104
|
+
spinner.succeed(chalk.green("Daemon started"));
|
|
105
|
+
console.log(chalk.gray(`PID: ${newStatus.pid}`));
|
|
106
|
+
const services = [];
|
|
107
|
+
if (newStatus.services.context.enabled) services.push("context");
|
|
108
|
+
if (newStatus.services.linear.enabled) services.push("linear");
|
|
109
|
+
if (newStatus.services.fileWatch.enabled) services.push("file-watch");
|
|
110
|
+
if (services.length > 0) {
|
|
111
|
+
console.log(chalk.gray(`Services: ${services.join(", ")}`));
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
spinner.fail(chalk.red("Failed to start daemon"));
|
|
115
|
+
console.log(chalk.gray("Check logs: stackmemory daemon logs"));
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
spinner.fail(chalk.red("Failed to start daemon"));
|
|
119
|
+
console.log(chalk.gray(error.message));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
cmd.command("stop").description("Stop the unified daemon").action(() => {
|
|
123
|
+
const status = readDaemonStatus();
|
|
124
|
+
if (!status.running || !status.pid) {
|
|
125
|
+
console.log(chalk.yellow("Daemon not running"));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
process.kill(status.pid, "SIGTERM");
|
|
130
|
+
console.log(chalk.green("Daemon stopped"));
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.log(chalk.red("Failed to stop daemon"));
|
|
133
|
+
console.log(chalk.gray(err.message));
|
|
134
|
+
const { pidFile } = getDaemonPaths();
|
|
135
|
+
if (existsSync(pidFile)) {
|
|
136
|
+
unlinkSync(pidFile);
|
|
137
|
+
console.log(chalk.gray("Cleaned up stale PID file"));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
cmd.command("restart").description("Restart the unified daemon").action(async () => {
|
|
142
|
+
const status = readDaemonStatus();
|
|
143
|
+
if (status.running && status.pid) {
|
|
144
|
+
try {
|
|
145
|
+
process.kill(status.pid, "SIGTERM");
|
|
146
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const config = loadDaemonConfig();
|
|
151
|
+
const daemonScript = getDaemonScriptPath();
|
|
152
|
+
if (!daemonScript) {
|
|
153
|
+
console.log(chalk.red("Daemon script not found"));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const args = [];
|
|
157
|
+
if (config.context.interval !== 15) {
|
|
158
|
+
args.push("--save-interval", String(config.context.interval));
|
|
159
|
+
}
|
|
160
|
+
if (!config.linear.enabled) {
|
|
161
|
+
args.push("--no-linear");
|
|
162
|
+
} else if (config.linear.interval !== 60) {
|
|
163
|
+
args.push("--linear-interval", String(config.linear.interval));
|
|
164
|
+
}
|
|
165
|
+
const daemonProcess = spawn("node", [daemonScript, ...args], {
|
|
166
|
+
detached: true,
|
|
167
|
+
stdio: "ignore"
|
|
168
|
+
});
|
|
169
|
+
daemonProcess.unref();
|
|
170
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
171
|
+
const newStatus = readDaemonStatus();
|
|
172
|
+
if (newStatus.running) {
|
|
173
|
+
console.log(
|
|
174
|
+
chalk.green("Daemon restarted"),
|
|
175
|
+
chalk.gray(`(pid: ${newStatus.pid})`)
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
console.log(chalk.red("Failed to restart daemon"));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
cmd.command("status").description("Check daemon status").action(() => {
|
|
182
|
+
const status = readDaemonStatus();
|
|
183
|
+
const config = loadDaemonConfig();
|
|
184
|
+
console.log(chalk.bold("\nStackMemory Unified Daemon\n"));
|
|
185
|
+
console.log(
|
|
186
|
+
`Status: ${status.running ? chalk.green("Running") : chalk.yellow("Stopped")}`
|
|
187
|
+
);
|
|
188
|
+
if (status.running) {
|
|
189
|
+
console.log(chalk.gray(` PID: ${status.pid}`));
|
|
190
|
+
if (status.uptime) {
|
|
191
|
+
const uptime = Math.round(status.uptime / 1e3);
|
|
192
|
+
const hours = Math.floor(uptime / 3600);
|
|
193
|
+
const mins = Math.floor(uptime % 3600 / 60);
|
|
194
|
+
const secs = uptime % 60;
|
|
195
|
+
console.log(chalk.gray(` Uptime: ${hours}h ${mins}m ${secs}s`));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
console.log("");
|
|
199
|
+
console.log(chalk.bold("Services:"));
|
|
200
|
+
const ctx = status.services.context;
|
|
201
|
+
console.log(
|
|
202
|
+
` Context: ${ctx.enabled ? chalk.green("Enabled") : chalk.gray("Disabled")}`
|
|
203
|
+
);
|
|
204
|
+
if (ctx.enabled) {
|
|
205
|
+
console.log(chalk.gray(` Interval: ${config.context.interval} min`));
|
|
206
|
+
if (ctx.saveCount) {
|
|
207
|
+
console.log(chalk.gray(` Saves: ${ctx.saveCount}`));
|
|
208
|
+
}
|
|
209
|
+
if (ctx.lastRun) {
|
|
210
|
+
const ago = Math.round((Date.now() - ctx.lastRun) / 1e3 / 60);
|
|
211
|
+
console.log(chalk.gray(` Last save: ${ago} min ago`));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const lin = status.services.linear;
|
|
215
|
+
console.log(
|
|
216
|
+
` Linear: ${lin.enabled ? chalk.green("Enabled") : chalk.gray("Disabled")}`
|
|
217
|
+
);
|
|
218
|
+
if (lin.enabled) {
|
|
219
|
+
console.log(chalk.gray(` Interval: ${config.linear.interval} min`));
|
|
220
|
+
if (config.linear.quietHours) {
|
|
221
|
+
console.log(
|
|
222
|
+
chalk.gray(
|
|
223
|
+
` Quiet hours: ${config.linear.quietHours.start}:00 - ${config.linear.quietHours.end}:00`
|
|
224
|
+
)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (lin.syncCount) {
|
|
228
|
+
console.log(chalk.gray(` Syncs: ${lin.syncCount}`));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const fw = status.services.fileWatch;
|
|
232
|
+
console.log(
|
|
233
|
+
` FileWatch: ${fw.enabled ? chalk.green("Enabled") : chalk.gray("Disabled")}`
|
|
234
|
+
);
|
|
235
|
+
if (status.errors && status.errors.length > 0) {
|
|
236
|
+
console.log("");
|
|
237
|
+
console.log(chalk.bold("Recent Errors:"));
|
|
238
|
+
status.errors.slice(-3).forEach((err) => {
|
|
239
|
+
console.log(chalk.red(` - ${err.slice(0, 80)}`));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (!status.running) {
|
|
243
|
+
console.log("");
|
|
244
|
+
console.log(chalk.bold("To start: stackmemory daemon start"));
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
cmd.command("logs").description("View daemon logs").option("-n, --lines <number>", "Number of lines to show", "50").option("-f, --follow", "Follow log output").option("--level <level>", "Filter by log level").action((options) => {
|
|
248
|
+
const { logFile } = getDaemonPaths();
|
|
249
|
+
if (!existsSync(logFile)) {
|
|
250
|
+
console.log(chalk.yellow("No log file found"));
|
|
251
|
+
console.log(
|
|
252
|
+
chalk.gray("Start the daemon first: stackmemory daemon start")
|
|
253
|
+
);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (options.follow) {
|
|
257
|
+
const tail = spawn("tail", ["-f", logFile], { stdio: "inherit" });
|
|
258
|
+
tail.on("error", () => {
|
|
259
|
+
console.log(chalk.red("Could not follow logs"));
|
|
260
|
+
});
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const content = readFileSync(logFile, "utf8");
|
|
264
|
+
const lines = content.trim().split("\n");
|
|
265
|
+
const count = parseInt(options.lines, 10);
|
|
266
|
+
let recent = lines.slice(-count);
|
|
267
|
+
if (options.level) {
|
|
268
|
+
const level = options.level.toUpperCase();
|
|
269
|
+
recent = recent.filter((line) => {
|
|
270
|
+
try {
|
|
271
|
+
const entry = JSON.parse(line);
|
|
272
|
+
return entry.level === level;
|
|
273
|
+
} catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
console.log(chalk.bold(`
|
|
279
|
+
Daemon logs (${recent.length} lines):
|
|
280
|
+
`));
|
|
281
|
+
for (const line of recent) {
|
|
282
|
+
try {
|
|
283
|
+
const entry = JSON.parse(line);
|
|
284
|
+
const time = entry.timestamp.split("T")[1].split(".")[0];
|
|
285
|
+
const levelColor = entry.level === "ERROR" ? chalk.red : entry.level === "WARN" ? chalk.yellow : entry.level === "DEBUG" ? chalk.gray : chalk.white;
|
|
286
|
+
console.log(
|
|
287
|
+
`${chalk.gray(time)} ${levelColor(`[${entry.level}]`)} ${chalk.cyan(`[${entry.service}]`)} ${entry.message}`
|
|
288
|
+
);
|
|
289
|
+
} catch {
|
|
290
|
+
console.log(line);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
cmd.command("config").description("Show or edit daemon configuration").option("--edit", "Open config in editor").option("--reset", "Reset to default configuration").option("--set <key=value>", "Set a config value").action((options) => {
|
|
295
|
+
const { configFile } = getDaemonPaths();
|
|
296
|
+
if (options.reset) {
|
|
297
|
+
saveDaemonConfig(DEFAULT_DAEMON_CONFIG);
|
|
298
|
+
console.log(chalk.green("Configuration reset to defaults"));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (options.edit) {
|
|
302
|
+
const editor = process.env["EDITOR"] || "vim";
|
|
303
|
+
spawn(editor, [configFile], { stdio: "inherit" });
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (options.set) {
|
|
307
|
+
const [key, value] = options.set.split("=");
|
|
308
|
+
const config2 = loadDaemonConfig();
|
|
309
|
+
const parts = key.split(".");
|
|
310
|
+
let target = config2;
|
|
311
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
312
|
+
if (target[parts[i]] && typeof target[parts[i]] === "object") {
|
|
313
|
+
target = target[parts[i]];
|
|
314
|
+
} else {
|
|
315
|
+
console.log(chalk.red(`Invalid config key: ${key}`));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const lastKey = parts[parts.length - 1];
|
|
320
|
+
const parsed = value === "true" ? true : value === "false" ? false : isNaN(Number(value)) ? value : Number(value);
|
|
321
|
+
target[lastKey] = parsed;
|
|
322
|
+
saveDaemonConfig(config2);
|
|
323
|
+
console.log(chalk.green(`Set ${key} = ${value}`));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const config = loadDaemonConfig();
|
|
327
|
+
console.log(chalk.bold("\nDaemon Configuration\n"));
|
|
328
|
+
console.log(chalk.gray(`File: ${configFile}`));
|
|
329
|
+
console.log("");
|
|
330
|
+
console.log(chalk.bold("Context Service:"));
|
|
331
|
+
console.log(` Enabled: ${config.context.enabled}`);
|
|
332
|
+
console.log(` Interval: ${config.context.interval} minutes`);
|
|
333
|
+
console.log("");
|
|
334
|
+
console.log(chalk.bold("Linear Service:"));
|
|
335
|
+
console.log(` Enabled: ${config.linear.enabled}`);
|
|
336
|
+
console.log(` Interval: ${config.linear.interval} minutes`);
|
|
337
|
+
if (config.linear.quietHours) {
|
|
338
|
+
console.log(
|
|
339
|
+
` Quiet hours: ${config.linear.quietHours.start}:00 - ${config.linear.quietHours.end}:00`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
console.log("");
|
|
343
|
+
console.log(chalk.bold("File Watch:"));
|
|
344
|
+
console.log(` Enabled: ${config.fileWatch.enabled}`);
|
|
345
|
+
console.log(` Extensions: ${config.fileWatch.extensions.join(", ")}`);
|
|
346
|
+
console.log("");
|
|
347
|
+
console.log(chalk.bold("General:"));
|
|
348
|
+
console.log(` Heartbeat: ${config.heartbeatInterval} seconds`);
|
|
349
|
+
console.log(` Log level: ${config.logLevel}`);
|
|
350
|
+
});
|
|
351
|
+
cmd.action(() => {
|
|
352
|
+
const status = readDaemonStatus();
|
|
353
|
+
console.log(chalk.bold("\nStackMemory Daemon\n"));
|
|
354
|
+
console.log(
|
|
355
|
+
`Status: ${status.running ? chalk.green("Running") : chalk.yellow("Stopped")}`
|
|
356
|
+
);
|
|
357
|
+
if (!status.running) {
|
|
358
|
+
console.log("");
|
|
359
|
+
console.log(chalk.bold("Quick start:"));
|
|
360
|
+
console.log(" stackmemory daemon start Start background services");
|
|
361
|
+
} else {
|
|
362
|
+
console.log("");
|
|
363
|
+
console.log(chalk.bold("Commands:"));
|
|
364
|
+
console.log(" stackmemory daemon status View detailed status");
|
|
365
|
+
console.log(" stackmemory daemon logs View daemon logs");
|
|
366
|
+
console.log(" stackmemory daemon stop Stop the daemon");
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return cmd;
|
|
370
|
+
}
|
|
371
|
+
function getDaemonScriptPath() {
|
|
372
|
+
const candidates = [
|
|
373
|
+
join(__dirname, "../../daemon/unified-daemon.js"),
|
|
374
|
+
join(process.cwd(), "dist/daemon/unified-daemon.js"),
|
|
375
|
+
join(
|
|
376
|
+
process.cwd(),
|
|
377
|
+
"node_modules/@stackmemoryai/stackmemory/dist/daemon/unified-daemon.js"
|
|
378
|
+
)
|
|
379
|
+
];
|
|
380
|
+
for (const candidate of candidates) {
|
|
381
|
+
if (existsSync(candidate)) {
|
|
382
|
+
return candidate;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return candidates[0];
|
|
386
|
+
}
|
|
387
|
+
var daemon_default = createDaemonCommand();
|
|
388
|
+
export {
|
|
389
|
+
createDaemonCommand,
|
|
390
|
+
daemon_default as default
|
|
391
|
+
};
|
|
392
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/cli/commands/daemon.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Daemon CLI Command\n * Manage StackMemory unified daemon\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { existsSync, readFileSync, unlinkSync } from 'fs';\nimport { spawn } from 'child_process';\nimport { join } from 'path';\nimport {\n loadDaemonConfig,\n saveDaemonConfig,\n readDaemonStatus,\n getDaemonPaths,\n DEFAULT_DAEMON_CONFIG,\n type DaemonConfig,\n} from '../../daemon/daemon-config.js';\n\nexport function createDaemonCommand(): Command {\n const cmd = new Command('daemon')\n .description('Manage StackMemory unified daemon for background services')\n .addHelpText(\n 'after',\n `\nExamples:\n stackmemory daemon start Start the daemon\n stackmemory daemon stop Stop the daemon\n stackmemory daemon status Check daemon status\n stackmemory daemon logs View daemon logs\n stackmemory daemon config Show/edit configuration\n\nThe daemon provides:\n - Context auto-save (default: every 15 minutes)\n - Linear sync (optional, if configured)\n - File watch (optional, for change detection)\n`\n );\n\n // Start command\n cmd\n .command('start')\n .description('Start the unified daemon')\n .option('--foreground', 'Run in foreground (for debugging)')\n .option('--save-interval <minutes>', 'Context save interval in minutes')\n .option('--linear-interval <minutes>', 'Linear sync interval in minutes')\n .option('--no-linear', 'Disable Linear sync')\n .option('--log-level <level>', 'Log level (debug|info|warn|error)')\n .action(async (options) => {\n const status = readDaemonStatus();\n\n if (status.running) {\n console.log(\n chalk.yellow('Daemon already running'),\n chalk.gray(`(pid: ${status.pid})`)\n );\n return;\n }\n\n const spinner = ora('Starting unified daemon...').start();\n\n try {\n // Build args\n const args = ['daemon-run'];\n\n if (options.saveInterval) {\n args.push('--save-interval', options.saveInterval);\n }\n if (options.linearInterval) {\n args.push('--linear-interval', options.linearInterval);\n }\n if (options.linear === false) {\n args.push('--no-linear');\n }\n if (options.logLevel) {\n args.push('--log-level', options.logLevel);\n }\n\n if (options.foreground) {\n spinner.stop();\n console.log(chalk.cyan('Running in foreground (Ctrl+C to stop)'));\n const { UnifiedDaemon } =\n await import('../../daemon/unified-daemon.js');\n const config: Partial<DaemonConfig> = {};\n if (options.saveInterval) {\n config.context = {\n enabled: true,\n interval: parseInt(options.saveInterval, 10),\n };\n }\n if (options.linearInterval) {\n config.linear = {\n enabled: true,\n interval: parseInt(options.linearInterval, 10),\n retryAttempts: 3,\n retryDelay: 30000,\n };\n }\n if (options.linear === false) {\n config.linear = {\n enabled: false,\n interval: 60,\n retryAttempts: 3,\n retryDelay: 30000,\n };\n }\n const daemon = new UnifiedDaemon(config);\n await daemon.start();\n return;\n }\n\n // Get path to daemon script\n const daemonScript = getDaemonScriptPath();\n if (!daemonScript) {\n spinner.fail(chalk.red('Daemon script not found'));\n return;\n }\n\n // Start in background\n const daemonProcess = spawn('node', [daemonScript, ...args.slice(1)], {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env },\n });\n\n daemonProcess.unref();\n\n // Wait for startup\n await new Promise((r) => setTimeout(r, 1000));\n const newStatus = readDaemonStatus();\n\n if (newStatus.running) {\n spinner.succeed(chalk.green('Daemon started'));\n console.log(chalk.gray(`PID: ${newStatus.pid}`));\n\n // Show enabled services\n const services = [];\n if (newStatus.services.context.enabled) services.push('context');\n if (newStatus.services.linear.enabled) services.push('linear');\n if (newStatus.services.fileWatch.enabled) services.push('file-watch');\n if (services.length > 0) {\n console.log(chalk.gray(`Services: ${services.join(', ')}`));\n }\n } else {\n spinner.fail(chalk.red('Failed to start daemon'));\n console.log(chalk.gray('Check logs: stackmemory daemon logs'));\n }\n } catch (error) {\n spinner.fail(chalk.red('Failed to start daemon'));\n console.log(chalk.gray((error as Error).message));\n }\n });\n\n // Stop command\n cmd\n .command('stop')\n .description('Stop the unified daemon')\n .action(() => {\n const status = readDaemonStatus();\n\n if (!status.running || !status.pid) {\n console.log(chalk.yellow('Daemon not running'));\n return;\n }\n\n try {\n process.kill(status.pid, 'SIGTERM');\n console.log(chalk.green('Daemon stopped'));\n } catch (err) {\n console.log(chalk.red('Failed to stop daemon'));\n console.log(chalk.gray((err as Error).message));\n\n // Clean up stale PID file\n const { pidFile } = getDaemonPaths();\n if (existsSync(pidFile)) {\n unlinkSync(pidFile);\n console.log(chalk.gray('Cleaned up stale PID file'));\n }\n }\n });\n\n // Restart command\n cmd\n .command('restart')\n .description('Restart the unified daemon')\n .action(async () => {\n const status = readDaemonStatus();\n\n if (status.running && status.pid) {\n try {\n process.kill(status.pid, 'SIGTERM');\n await new Promise((r) => setTimeout(r, 1000));\n } catch {\n // Ignore\n }\n }\n\n // Get saved config\n const config = loadDaemonConfig();\n\n // Start with same config\n const daemonScript = getDaemonScriptPath();\n if (!daemonScript) {\n console.log(chalk.red('Daemon script not found'));\n return;\n }\n\n const args: string[] = [];\n if (config.context.interval !== 15) {\n args.push('--save-interval', String(config.context.interval));\n }\n if (!config.linear.enabled) {\n args.push('--no-linear');\n } else if (config.linear.interval !== 60) {\n args.push('--linear-interval', String(config.linear.interval));\n }\n\n const daemonProcess = spawn('node', [daemonScript, ...args], {\n detached: true,\n stdio: 'ignore',\n });\n daemonProcess.unref();\n\n await new Promise((r) => setTimeout(r, 1000));\n const newStatus = readDaemonStatus();\n\n if (newStatus.running) {\n console.log(\n chalk.green('Daemon restarted'),\n chalk.gray(`(pid: ${newStatus.pid})`)\n );\n } else {\n console.log(chalk.red('Failed to restart daemon'));\n }\n });\n\n // Status command\n cmd\n .command('status')\n .description('Check daemon status')\n .action(() => {\n const status = readDaemonStatus();\n const config = loadDaemonConfig();\n\n console.log(chalk.bold('\\nStackMemory Unified Daemon\\n'));\n\n console.log(\n `Status: ${status.running ? chalk.green('Running') : chalk.yellow('Stopped')}`\n );\n\n if (status.running) {\n console.log(chalk.gray(` PID: ${status.pid}`));\n if (status.uptime) {\n const uptime = Math.round(status.uptime / 1000);\n const hours = Math.floor(uptime / 3600);\n const mins = Math.floor((uptime % 3600) / 60);\n const secs = uptime % 60;\n console.log(chalk.gray(` Uptime: ${hours}h ${mins}m ${secs}s`));\n }\n }\n\n console.log('');\n console.log(chalk.bold('Services:'));\n\n // Context service\n const ctx = status.services.context;\n console.log(\n ` Context: ${ctx.enabled ? chalk.green('Enabled') : chalk.gray('Disabled')}`\n );\n if (ctx.enabled) {\n console.log(chalk.gray(` Interval: ${config.context.interval} min`));\n if (ctx.saveCount) {\n console.log(chalk.gray(` Saves: ${ctx.saveCount}`));\n }\n if (ctx.lastRun) {\n const ago = Math.round((Date.now() - ctx.lastRun) / 1000 / 60);\n console.log(chalk.gray(` Last save: ${ago} min ago`));\n }\n }\n\n // Linear service\n const lin = status.services.linear;\n console.log(\n ` Linear: ${lin.enabled ? chalk.green('Enabled') : chalk.gray('Disabled')}`\n );\n if (lin.enabled) {\n console.log(chalk.gray(` Interval: ${config.linear.interval} min`));\n if (config.linear.quietHours) {\n console.log(\n chalk.gray(\n ` Quiet hours: ${config.linear.quietHours.start}:00 - ${config.linear.quietHours.end}:00`\n )\n );\n }\n if (lin.syncCount) {\n console.log(chalk.gray(` Syncs: ${lin.syncCount}`));\n }\n }\n\n // File watch\n const fw = status.services.fileWatch;\n console.log(\n ` FileWatch: ${fw.enabled ? chalk.green('Enabled') : chalk.gray('Disabled')}`\n );\n\n // Errors\n if (status.errors && status.errors.length > 0) {\n console.log('');\n console.log(chalk.bold('Recent Errors:'));\n status.errors.slice(-3).forEach((err) => {\n console.log(chalk.red(` - ${err.slice(0, 80)}`));\n });\n }\n\n if (!status.running) {\n console.log('');\n console.log(chalk.bold('To start: stackmemory daemon start'));\n }\n });\n\n // Logs command\n cmd\n .command('logs')\n .description('View daemon logs')\n .option('-n, --lines <number>', 'Number of lines to show', '50')\n .option('-f, --follow', 'Follow log output')\n .option('--level <level>', 'Filter by log level')\n .action((options) => {\n const { logFile } = getDaemonPaths();\n\n if (!existsSync(logFile)) {\n console.log(chalk.yellow('No log file found'));\n console.log(\n chalk.gray('Start the daemon first: stackmemory daemon start')\n );\n return;\n }\n\n if (options.follow) {\n const tail = spawn('tail', ['-f', logFile], { stdio: 'inherit' });\n tail.on('error', () => {\n console.log(chalk.red('Could not follow logs'));\n });\n return;\n }\n\n const content = readFileSync(logFile, 'utf8');\n const lines = content.trim().split('\\n');\n const count = parseInt(options.lines, 10);\n let recent = lines.slice(-count);\n\n // Filter by level if specified\n if (options.level) {\n const level = options.level.toUpperCase();\n recent = recent.filter((line) => {\n try {\n const entry = JSON.parse(line);\n return entry.level === level;\n } catch {\n return false;\n }\n });\n }\n\n console.log(chalk.bold(`\\nDaemon logs (${recent.length} lines):\\n`));\n\n for (const line of recent) {\n try {\n const entry = JSON.parse(line);\n const time = entry.timestamp.split('T')[1].split('.')[0];\n const levelColor =\n entry.level === 'ERROR'\n ? chalk.red\n : entry.level === 'WARN'\n ? chalk.yellow\n : entry.level === 'DEBUG'\n ? chalk.gray\n : chalk.white;\n\n console.log(\n `${chalk.gray(time)} ${levelColor(`[${entry.level}]`)} ${chalk.cyan(`[${entry.service}]`)} ${entry.message}`\n );\n } catch {\n console.log(line);\n }\n }\n });\n\n // Config command\n cmd\n .command('config')\n .description('Show or edit daemon configuration')\n .option('--edit', 'Open config in editor')\n .option('--reset', 'Reset to default configuration')\n .option('--set <key=value>', 'Set a config value')\n .action((options) => {\n const { configFile } = getDaemonPaths();\n\n if (options.reset) {\n saveDaemonConfig(DEFAULT_DAEMON_CONFIG);\n console.log(chalk.green('Configuration reset to defaults'));\n return;\n }\n\n if (options.edit) {\n const editor = process.env['EDITOR'] || 'vim';\n spawn(editor, [configFile], { stdio: 'inherit' });\n return;\n }\n\n if (options.set) {\n const [key, value] = options.set.split('=');\n const config = loadDaemonConfig();\n\n // Parse the key path (e.g., \"context.interval\")\n const parts = key.split('.');\n let target: Record<string, unknown> = config as unknown as Record<\n string,\n unknown\n >;\n for (let i = 0; i < parts.length - 1; i++) {\n if (target[parts[i]] && typeof target[parts[i]] === 'object') {\n target = target[parts[i]] as Record<string, unknown>;\n } else {\n console.log(chalk.red(`Invalid config key: ${key}`));\n return;\n }\n }\n\n const lastKey = parts[parts.length - 1];\n const parsed =\n value === 'true'\n ? true\n : value === 'false'\n ? false\n : isNaN(Number(value))\n ? value\n : Number(value);\n target[lastKey] = parsed;\n\n saveDaemonConfig(config);\n console.log(chalk.green(`Set ${key} = ${value}`));\n return;\n }\n\n // Show config\n const config = loadDaemonConfig();\n\n console.log(chalk.bold('\\nDaemon Configuration\\n'));\n console.log(chalk.gray(`File: ${configFile}`));\n console.log('');\n\n console.log(chalk.bold('Context Service:'));\n console.log(` Enabled: ${config.context.enabled}`);\n console.log(` Interval: ${config.context.interval} minutes`);\n\n console.log('');\n console.log(chalk.bold('Linear Service:'));\n console.log(` Enabled: ${config.linear.enabled}`);\n console.log(` Interval: ${config.linear.interval} minutes`);\n if (config.linear.quietHours) {\n console.log(\n ` Quiet hours: ${config.linear.quietHours.start}:00 - ${config.linear.quietHours.end}:00`\n );\n }\n\n console.log('');\n console.log(chalk.bold('File Watch:'));\n console.log(` Enabled: ${config.fileWatch.enabled}`);\n console.log(` Extensions: ${config.fileWatch.extensions.join(', ')}`);\n\n console.log('');\n console.log(chalk.bold('General:'));\n console.log(` Heartbeat: ${config.heartbeatInterval} seconds`);\n console.log(` Log level: ${config.logLevel}`);\n });\n\n // Default action\n cmd.action(() => {\n const status = readDaemonStatus();\n\n console.log(chalk.bold('\\nStackMemory Daemon\\n'));\n console.log(\n `Status: ${status.running ? chalk.green('Running') : chalk.yellow('Stopped')}`\n );\n\n if (!status.running) {\n console.log('');\n console.log(chalk.bold('Quick start:'));\n console.log(' stackmemory daemon start Start background services');\n } else {\n console.log('');\n console.log(chalk.bold('Commands:'));\n console.log(' stackmemory daemon status View detailed status');\n console.log(' stackmemory daemon logs View daemon logs');\n console.log(' stackmemory daemon stop Stop the daemon');\n }\n });\n\n return cmd;\n}\n\n/**\n * Get path to daemon script\n */\nfunction getDaemonScriptPath(): string | null {\n // Check various locations\n const candidates = [\n join(__dirname, '../../daemon/unified-daemon.js'),\n join(process.cwd(), 'dist/daemon/unified-daemon.js'),\n join(\n process.cwd(),\n 'node_modules/@stackmemoryai/stackmemory/dist/daemon/unified-daemon.js'\n ),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return candidates[0]; // Return first candidate as fallback\n}\n\nexport default createDaemonCommand();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["config"]
|
|
7
|
+
}
|
|
@@ -580,6 +580,59 @@ Full log: ${logFile}`));
|
|
|
580
580
|
console.log(chalk.red(`Failed to read logs: ${err.message}`));
|
|
581
581
|
}
|
|
582
582
|
}
|
|
583
|
+
async function installServiceSilent() {
|
|
584
|
+
try {
|
|
585
|
+
const config = getServiceConfig();
|
|
586
|
+
if (config.platform === "unsupported") {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
const home = process.env.HOME || "";
|
|
590
|
+
await fs.mkdir(config.serviceDir, { recursive: true });
|
|
591
|
+
await fs.mkdir(config.logDir, { recursive: true });
|
|
592
|
+
const guardianPath = path.join(home, ".stackmemory", "guardian.js");
|
|
593
|
+
await fs.writeFile(guardianPath, generateGuardianScript(), "utf-8");
|
|
594
|
+
await fs.chmod(guardianPath, 493);
|
|
595
|
+
if (config.platform === "darwin") {
|
|
596
|
+
const plistContent = generateMacOSPlist(config);
|
|
597
|
+
await fs.writeFile(config.serviceFile, plistContent, "utf-8");
|
|
598
|
+
try {
|
|
599
|
+
execSync(`launchctl load -w "${config.serviceFile}"`, {
|
|
600
|
+
stdio: "pipe"
|
|
601
|
+
});
|
|
602
|
+
} catch {
|
|
603
|
+
try {
|
|
604
|
+
execSync(`launchctl unload "${config.serviceFile}"`, {
|
|
605
|
+
stdio: "pipe"
|
|
606
|
+
});
|
|
607
|
+
execSync(`launchctl load -w "${config.serviceFile}"`, {
|
|
608
|
+
stdio: "pipe"
|
|
609
|
+
});
|
|
610
|
+
} catch {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return true;
|
|
615
|
+
} else if (config.platform === "linux") {
|
|
616
|
+
const serviceContent = generateLinuxSystemdService(config);
|
|
617
|
+
await fs.writeFile(config.serviceFile, serviceContent, "utf-8");
|
|
618
|
+
try {
|
|
619
|
+
execSync("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
620
|
+
execSync(`systemctl --user enable ${config.serviceName}`, {
|
|
621
|
+
stdio: "pipe"
|
|
622
|
+
});
|
|
623
|
+
execSync(`systemctl --user start ${config.serviceName}`, {
|
|
624
|
+
stdio: "pipe"
|
|
625
|
+
});
|
|
626
|
+
} catch {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
return false;
|
|
632
|
+
} catch {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
583
636
|
function createServiceCommand() {
|
|
584
637
|
const cmd = new Command("service").description("Manage StackMemory guardian OS service (auto-start on login)").addHelpText(
|
|
585
638
|
"after",
|
|
@@ -690,6 +743,7 @@ The guardian service:
|
|
|
690
743
|
var service_default = createServiceCommand();
|
|
691
744
|
export {
|
|
692
745
|
createServiceCommand,
|
|
693
|
-
service_default as default
|
|
746
|
+
service_default as default,
|
|
747
|
+
installServiceSilent
|
|
694
748
|
};
|
|
695
749
|
//# sourceMappingURL=service.js.map
|