@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
|
@@ -0,0 +1,149 @@
|
|
|
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 { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
const DEFAULT_DAEMON_CONFIG = {
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
context: {
|
|
11
|
+
enabled: true,
|
|
12
|
+
interval: 15,
|
|
13
|
+
// 15 minutes
|
|
14
|
+
checkpointMessage: "Auto-checkpoint"
|
|
15
|
+
},
|
|
16
|
+
linear: {
|
|
17
|
+
enabled: false,
|
|
18
|
+
// Disabled by default, requires setup
|
|
19
|
+
interval: 60,
|
|
20
|
+
// 60 minutes
|
|
21
|
+
quietHours: { start: 22, end: 7 },
|
|
22
|
+
retryAttempts: 3,
|
|
23
|
+
retryDelay: 3e4
|
|
24
|
+
},
|
|
25
|
+
fileWatch: {
|
|
26
|
+
enabled: false,
|
|
27
|
+
// Disabled by default
|
|
28
|
+
interval: 0,
|
|
29
|
+
// Not interval-based
|
|
30
|
+
paths: ["."],
|
|
31
|
+
extensions: [".ts", ".js", ".tsx", ".jsx", ".py", ".go", ".rs"],
|
|
32
|
+
ignore: ["node_modules", ".git", "dist", "build", ".stackmemory"],
|
|
33
|
+
debounceMs: 2e3
|
|
34
|
+
},
|
|
35
|
+
heartbeatInterval: 60,
|
|
36
|
+
// 1 minute
|
|
37
|
+
inactivityTimeout: 0,
|
|
38
|
+
// Disabled by default
|
|
39
|
+
logLevel: "info"
|
|
40
|
+
};
|
|
41
|
+
function getDaemonDir() {
|
|
42
|
+
const dir = join(homedir(), ".stackmemory", "daemon");
|
|
43
|
+
if (!existsSync(dir)) {
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
return dir;
|
|
47
|
+
}
|
|
48
|
+
function getLogsDir() {
|
|
49
|
+
const dir = join(homedir(), ".stackmemory", "logs");
|
|
50
|
+
if (!existsSync(dir)) {
|
|
51
|
+
mkdirSync(dir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
return dir;
|
|
54
|
+
}
|
|
55
|
+
function getDaemonPaths() {
|
|
56
|
+
const daemonDir = getDaemonDir();
|
|
57
|
+
const logsDir = getLogsDir();
|
|
58
|
+
return {
|
|
59
|
+
pidFile: join(daemonDir, "daemon.pid"),
|
|
60
|
+
statusFile: join(daemonDir, "daemon.status"),
|
|
61
|
+
configFile: join(daemonDir, "config.json"),
|
|
62
|
+
logFile: join(logsDir, "daemon.log")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function loadDaemonConfig() {
|
|
66
|
+
const { configFile } = getDaemonPaths();
|
|
67
|
+
if (!existsSync(configFile)) {
|
|
68
|
+
return { ...DEFAULT_DAEMON_CONFIG };
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const content = readFileSync(configFile, "utf8");
|
|
72
|
+
const config = JSON.parse(content);
|
|
73
|
+
return {
|
|
74
|
+
...DEFAULT_DAEMON_CONFIG,
|
|
75
|
+
...config,
|
|
76
|
+
context: { ...DEFAULT_DAEMON_CONFIG.context, ...config.context },
|
|
77
|
+
linear: { ...DEFAULT_DAEMON_CONFIG.linear, ...config.linear },
|
|
78
|
+
fileWatch: { ...DEFAULT_DAEMON_CONFIG.fileWatch, ...config.fileWatch }
|
|
79
|
+
};
|
|
80
|
+
} catch {
|
|
81
|
+
return { ...DEFAULT_DAEMON_CONFIG };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function saveDaemonConfig(config) {
|
|
85
|
+
const { configFile } = getDaemonPaths();
|
|
86
|
+
const currentConfig = loadDaemonConfig();
|
|
87
|
+
const newConfig = {
|
|
88
|
+
...currentConfig,
|
|
89
|
+
...config,
|
|
90
|
+
context: { ...currentConfig.context, ...config.context },
|
|
91
|
+
linear: { ...currentConfig.linear, ...config.linear },
|
|
92
|
+
fileWatch: { ...currentConfig.fileWatch, ...config.fileWatch }
|
|
93
|
+
};
|
|
94
|
+
writeFileSync(configFile, JSON.stringify(newConfig, null, 2));
|
|
95
|
+
}
|
|
96
|
+
function readDaemonStatus() {
|
|
97
|
+
const { statusFile, pidFile } = getDaemonPaths();
|
|
98
|
+
const defaultStatus = {
|
|
99
|
+
running: false,
|
|
100
|
+
services: {
|
|
101
|
+
context: { enabled: false },
|
|
102
|
+
linear: { enabled: false },
|
|
103
|
+
fileWatch: { enabled: false }
|
|
104
|
+
},
|
|
105
|
+
errors: []
|
|
106
|
+
};
|
|
107
|
+
if (!existsSync(pidFile)) {
|
|
108
|
+
return defaultStatus;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const pidContent = readFileSync(pidFile, "utf8").trim();
|
|
112
|
+
const pid = parseInt(pidContent, 10);
|
|
113
|
+
try {
|
|
114
|
+
process.kill(pid, 0);
|
|
115
|
+
} catch {
|
|
116
|
+
return defaultStatus;
|
|
117
|
+
}
|
|
118
|
+
if (!existsSync(statusFile)) {
|
|
119
|
+
return { ...defaultStatus, running: true, pid };
|
|
120
|
+
}
|
|
121
|
+
const content = readFileSync(statusFile, "utf8");
|
|
122
|
+
const status = JSON.parse(content);
|
|
123
|
+
return {
|
|
124
|
+
...status,
|
|
125
|
+
running: true,
|
|
126
|
+
pid,
|
|
127
|
+
uptime: status.startTime ? Date.now() - status.startTime : void 0
|
|
128
|
+
};
|
|
129
|
+
} catch {
|
|
130
|
+
return defaultStatus;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function writeDaemonStatus(status) {
|
|
134
|
+
const { statusFile } = getDaemonPaths();
|
|
135
|
+
const currentStatus = readDaemonStatus();
|
|
136
|
+
const newStatus = { ...currentStatus, ...status };
|
|
137
|
+
writeFileSync(statusFile, JSON.stringify(newStatus, null, 2));
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
DEFAULT_DAEMON_CONFIG,
|
|
141
|
+
getDaemonDir,
|
|
142
|
+
getDaemonPaths,
|
|
143
|
+
getLogsDir,
|
|
144
|
+
loadDaemonConfig,
|
|
145
|
+
readDaemonStatus,
|
|
146
|
+
saveDaemonConfig,
|
|
147
|
+
writeDaemonStatus
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=daemon-config.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/daemon/daemon-config.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Daemon Configuration Management\n * Handles loading, saving, and validating daemon configuration\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport interface DaemonServiceConfig {\n enabled: boolean;\n interval: number; // minutes\n}\n\nexport interface ContextServiceConfig extends DaemonServiceConfig {\n checkpointMessage?: string;\n}\n\nexport interface LinearServiceConfig extends DaemonServiceConfig {\n quietHours?: {\n start: number; // hour 0-23\n end: number;\n };\n retryAttempts: number;\n retryDelay: number; // ms\n}\n\nexport interface FileWatchConfig extends DaemonServiceConfig {\n paths: string[];\n extensions: string[];\n ignore: string[];\n debounceMs: number;\n}\n\nexport interface DaemonConfig {\n version: string;\n context: ContextServiceConfig;\n linear: LinearServiceConfig;\n fileWatch: FileWatchConfig;\n heartbeatInterval: number; // seconds\n inactivityTimeout: number; // minutes, 0 = disabled\n logLevel: 'debug' | 'info' | 'warn' | 'error';\n}\n\nexport const DEFAULT_DAEMON_CONFIG: DaemonConfig = {\n version: '1.0.0',\n context: {\n enabled: true,\n interval: 15, // 15 minutes\n checkpointMessage: 'Auto-checkpoint',\n },\n linear: {\n enabled: false, // Disabled by default, requires setup\n interval: 60, // 60 minutes\n quietHours: { start: 22, end: 7 },\n retryAttempts: 3,\n retryDelay: 30000,\n },\n fileWatch: {\n enabled: false, // Disabled by default\n interval: 0, // Not interval-based\n paths: ['.'],\n extensions: ['.ts', '.js', '.tsx', '.jsx', '.py', '.go', '.rs'],\n ignore: ['node_modules', '.git', 'dist', 'build', '.stackmemory'],\n debounceMs: 2000,\n },\n heartbeatInterval: 60, // 1 minute\n inactivityTimeout: 0, // Disabled by default\n logLevel: 'info',\n};\n\nexport interface DaemonStatus {\n running: boolean;\n pid?: number;\n startTime?: number;\n uptime?: number;\n services: {\n context: { enabled: boolean; lastRun?: number; saveCount?: number };\n linear: { enabled: boolean; lastRun?: number; syncCount?: number };\n fileWatch: { enabled: boolean; eventsProcessed?: number };\n };\n errors: string[];\n}\n\n/**\n * Get the daemon directory path\n */\nexport function getDaemonDir(): string {\n const dir = join(homedir(), '.stackmemory', 'daemon');\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\n/**\n * Get the logs directory path\n */\nexport function getLogsDir(): string {\n const dir = join(homedir(), '.stackmemory', 'logs');\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\n/**\n * Get daemon file paths\n */\nexport function getDaemonPaths() {\n const daemonDir = getDaemonDir();\n const logsDir = getLogsDir();\n return {\n pidFile: join(daemonDir, 'daemon.pid'),\n statusFile: join(daemonDir, 'daemon.status'),\n configFile: join(daemonDir, 'config.json'),\n logFile: join(logsDir, 'daemon.log'),\n };\n}\n\n/**\n * Load daemon configuration\n */\nexport function loadDaemonConfig(): DaemonConfig {\n const { configFile } = getDaemonPaths();\n\n if (!existsSync(configFile)) {\n return { ...DEFAULT_DAEMON_CONFIG };\n }\n\n try {\n const content = readFileSync(configFile, 'utf8');\n const config = JSON.parse(content) as Partial<DaemonConfig>;\n return {\n ...DEFAULT_DAEMON_CONFIG,\n ...config,\n context: { ...DEFAULT_DAEMON_CONFIG.context, ...config.context },\n linear: { ...DEFAULT_DAEMON_CONFIG.linear, ...config.linear },\n fileWatch: { ...DEFAULT_DAEMON_CONFIG.fileWatch, ...config.fileWatch },\n };\n } catch {\n return { ...DEFAULT_DAEMON_CONFIG };\n }\n}\n\n/**\n * Save daemon configuration\n */\nexport function saveDaemonConfig(config: Partial<DaemonConfig>): void {\n const { configFile } = getDaemonPaths();\n const currentConfig = loadDaemonConfig();\n const newConfig = {\n ...currentConfig,\n ...config,\n context: { ...currentConfig.context, ...config.context },\n linear: { ...currentConfig.linear, ...config.linear },\n fileWatch: { ...currentConfig.fileWatch, ...config.fileWatch },\n };\n writeFileSync(configFile, JSON.stringify(newConfig, null, 2));\n}\n\n/**\n * Read daemon status\n */\nexport function readDaemonStatus(): DaemonStatus {\n const { statusFile, pidFile } = getDaemonPaths();\n\n const defaultStatus: DaemonStatus = {\n running: false,\n services: {\n context: { enabled: false },\n linear: { enabled: false },\n fileWatch: { enabled: false },\n },\n errors: [],\n };\n\n // Check PID file first\n if (!existsSync(pidFile)) {\n return defaultStatus;\n }\n\n try {\n const pidContent = readFileSync(pidFile, 'utf8').trim();\n const pid = parseInt(pidContent, 10);\n\n // Check if process is running\n try {\n process.kill(pid, 0);\n } catch {\n // Process not running\n return defaultStatus;\n }\n\n // Read status file\n if (!existsSync(statusFile)) {\n return { ...defaultStatus, running: true, pid };\n }\n\n const content = readFileSync(statusFile, 'utf8');\n const status = JSON.parse(content) as DaemonStatus;\n return {\n ...status,\n running: true,\n pid,\n uptime: status.startTime ? Date.now() - status.startTime : undefined,\n };\n } catch {\n return defaultStatus;\n }\n}\n\n/**\n * Write daemon status\n */\nexport function writeDaemonStatus(status: Partial<DaemonStatus>): void {\n const { statusFile } = getDaemonPaths();\n const currentStatus = readDaemonStatus();\n const newStatus = { ...currentStatus, ...status };\n writeFileSync(statusFile, JSON.stringify(newStatus, null, 2));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
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 { existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
class DaemonContextService {
|
|
10
|
+
config;
|
|
11
|
+
state;
|
|
12
|
+
intervalId;
|
|
13
|
+
isRunning = false;
|
|
14
|
+
onLog;
|
|
15
|
+
constructor(config, onLog) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.onLog = onLog;
|
|
18
|
+
this.state = {
|
|
19
|
+
lastSaveTime: 0,
|
|
20
|
+
saveCount: 0,
|
|
21
|
+
errors: []
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
start() {
|
|
25
|
+
if (this.isRunning || !this.config.enabled) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.isRunning = true;
|
|
29
|
+
const intervalMs = this.config.interval * 60 * 1e3;
|
|
30
|
+
this.onLog("INFO", "Context service started", {
|
|
31
|
+
interval: this.config.interval
|
|
32
|
+
});
|
|
33
|
+
this.saveContext();
|
|
34
|
+
this.intervalId = setInterval(() => {
|
|
35
|
+
this.saveContext();
|
|
36
|
+
}, intervalMs);
|
|
37
|
+
}
|
|
38
|
+
stop() {
|
|
39
|
+
if (this.intervalId) {
|
|
40
|
+
clearInterval(this.intervalId);
|
|
41
|
+
this.intervalId = void 0;
|
|
42
|
+
}
|
|
43
|
+
this.isRunning = false;
|
|
44
|
+
this.onLog("INFO", "Context service stopped");
|
|
45
|
+
}
|
|
46
|
+
getState() {
|
|
47
|
+
return { ...this.state };
|
|
48
|
+
}
|
|
49
|
+
updateConfig(config) {
|
|
50
|
+
const wasRunning = this.isRunning;
|
|
51
|
+
if (wasRunning) {
|
|
52
|
+
this.stop();
|
|
53
|
+
}
|
|
54
|
+
this.config = { ...this.config, ...config };
|
|
55
|
+
if (wasRunning && this.config.enabled) {
|
|
56
|
+
this.start();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
forceSave() {
|
|
60
|
+
this.saveContext();
|
|
61
|
+
}
|
|
62
|
+
saveContext() {
|
|
63
|
+
if (!this.isRunning) return;
|
|
64
|
+
try {
|
|
65
|
+
const stackmemoryBin = this.getStackMemoryBin();
|
|
66
|
+
if (!stackmemoryBin) {
|
|
67
|
+
this.onLog("WARN", "StackMemory binary not found");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const message = this.config.checkpointMessage || `Auto-checkpoint #${this.state.saveCount + 1}`;
|
|
71
|
+
const fullMessage = `${message} at ${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
72
|
+
execSync(`"${stackmemoryBin}" context add observation "${fullMessage}"`, {
|
|
73
|
+
timeout: 3e4,
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
stdio: "pipe"
|
|
76
|
+
});
|
|
77
|
+
this.state.saveCount++;
|
|
78
|
+
this.state.lastSaveTime = Date.now();
|
|
79
|
+
this.onLog("INFO", "Context saved", {
|
|
80
|
+
saveCount: this.state.saveCount
|
|
81
|
+
});
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
84
|
+
if (!errorMsg.includes("EBUSY") && !errorMsg.includes("EAGAIN")) {
|
|
85
|
+
this.state.errors.push(errorMsg);
|
|
86
|
+
this.onLog("WARN", "Failed to save context", { error: errorMsg });
|
|
87
|
+
if (this.state.errors.length > 10) {
|
|
88
|
+
this.state.errors = this.state.errors.slice(-10);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
getStackMemoryBin() {
|
|
94
|
+
const homeDir = homedir();
|
|
95
|
+
const locations = [
|
|
96
|
+
join(homeDir, ".stackmemory", "bin", "stackmemory"),
|
|
97
|
+
join(homeDir, ".local", "bin", "stackmemory"),
|
|
98
|
+
"/usr/local/bin/stackmemory",
|
|
99
|
+
"/opt/homebrew/bin/stackmemory"
|
|
100
|
+
];
|
|
101
|
+
for (const loc of locations) {
|
|
102
|
+
if (existsSync(loc)) {
|
|
103
|
+
return loc;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = execSync("which stackmemory", {
|
|
108
|
+
encoding: "utf8",
|
|
109
|
+
stdio: "pipe"
|
|
110
|
+
}).trim();
|
|
111
|
+
if (result && existsSync(result)) {
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
DaemonContextService
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=context-service.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/daemon/services/context-service.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Context Auto-Save Service\n * Periodically saves context checkpoints\n */\n\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport { homedir } from 'os';\nimport type { ContextServiceConfig } from '../daemon-config.js';\n\nexport interface ContextServiceState {\n lastSaveTime: number;\n saveCount: number;\n errors: string[];\n}\n\nexport class DaemonContextService {\n private config: ContextServiceConfig;\n private state: ContextServiceState;\n private intervalId?: NodeJS.Timeout;\n private isRunning = false;\n private onLog: (level: string, message: string, data?: unknown) => void;\n\n constructor(\n config: ContextServiceConfig,\n onLog: (level: string, message: string, data?: unknown) => void\n ) {\n this.config = config;\n this.onLog = onLog;\n this.state = {\n lastSaveTime: 0,\n saveCount: 0,\n errors: [],\n };\n }\n\n start(): void {\n if (this.isRunning || !this.config.enabled) {\n return;\n }\n\n this.isRunning = true;\n const intervalMs = this.config.interval * 60 * 1000;\n\n this.onLog('INFO', 'Context service started', {\n interval: this.config.interval,\n });\n\n // Initial save\n this.saveContext();\n\n // Schedule periodic saves\n this.intervalId = setInterval(() => {\n this.saveContext();\n }, intervalMs);\n }\n\n stop(): void {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n this.isRunning = false;\n this.onLog('INFO', 'Context service stopped');\n }\n\n getState(): ContextServiceState {\n return { ...this.state };\n }\n\n updateConfig(config: Partial<ContextServiceConfig>): void {\n const wasRunning = this.isRunning;\n if (wasRunning) {\n this.stop();\n }\n\n this.config = { ...this.config, ...config };\n\n if (wasRunning && this.config.enabled) {\n this.start();\n }\n }\n\n forceSave(): void {\n this.saveContext();\n }\n\n private saveContext(): void {\n if (!this.isRunning) return;\n\n try {\n const stackmemoryBin = this.getStackMemoryBin();\n\n if (!stackmemoryBin) {\n this.onLog('WARN', 'StackMemory binary not found');\n return;\n }\n\n const message =\n this.config.checkpointMessage ||\n `Auto-checkpoint #${this.state.saveCount + 1}`;\n const fullMessage = `${message} at ${new Date().toISOString()}`;\n\n execSync(`\"${stackmemoryBin}\" context add observation \"${fullMessage}\"`, {\n timeout: 30000,\n encoding: 'utf8',\n stdio: 'pipe',\n });\n\n this.state.saveCount++;\n this.state.lastSaveTime = Date.now();\n\n this.onLog('INFO', 'Context saved', {\n saveCount: this.state.saveCount,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n\n // Only log if not a transient error\n if (!errorMsg.includes('EBUSY') && !errorMsg.includes('EAGAIN')) {\n this.state.errors.push(errorMsg);\n this.onLog('WARN', 'Failed to save context', { error: errorMsg });\n\n // Keep only last 10 errors\n if (this.state.errors.length > 10) {\n this.state.errors = this.state.errors.slice(-10);\n }\n }\n }\n }\n\n private getStackMemoryBin(): string | null {\n const homeDir = homedir();\n\n // Check common locations\n const locations = [\n join(homeDir, '.stackmemory', 'bin', 'stackmemory'),\n join(homeDir, '.local', 'bin', 'stackmemory'),\n '/usr/local/bin/stackmemory',\n '/opt/homebrew/bin/stackmemory',\n ];\n\n for (const loc of locations) {\n if (existsSync(loc)) {\n return loc;\n }\n }\n\n // Try to find in PATH\n try {\n const result = execSync('which stackmemory', {\n encoding: 'utf8',\n stdio: 'pipe',\n }).trim();\n if (result && existsSync(result)) {\n return result;\n }\n } catch {\n // Not in PATH\n }\n\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
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 { existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
class DaemonLinearService {
|
|
9
|
+
config;
|
|
10
|
+
state;
|
|
11
|
+
intervalId;
|
|
12
|
+
isRunning = false;
|
|
13
|
+
onLog;
|
|
14
|
+
constructor(config, onLog) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.onLog = onLog;
|
|
17
|
+
this.state = {
|
|
18
|
+
lastSyncTime: 0,
|
|
19
|
+
syncCount: 0,
|
|
20
|
+
errors: []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async start() {
|
|
24
|
+
if (this.isRunning || !this.config.enabled) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!this.isLinearConfigured()) {
|
|
28
|
+
this.onLog("WARN", "Linear not configured, skipping linear service");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.isRunning = true;
|
|
32
|
+
const intervalMs = this.config.interval * 60 * 1e3;
|
|
33
|
+
this.onLog("INFO", "Linear service started", {
|
|
34
|
+
interval: this.config.interval,
|
|
35
|
+
quietHours: this.config.quietHours
|
|
36
|
+
});
|
|
37
|
+
await this.performSync();
|
|
38
|
+
this.intervalId = setInterval(async () => {
|
|
39
|
+
await this.performSync();
|
|
40
|
+
}, intervalMs);
|
|
41
|
+
}
|
|
42
|
+
stop() {
|
|
43
|
+
if (this.intervalId) {
|
|
44
|
+
clearInterval(this.intervalId);
|
|
45
|
+
this.intervalId = void 0;
|
|
46
|
+
}
|
|
47
|
+
this.isRunning = false;
|
|
48
|
+
this.onLog("INFO", "Linear service stopped");
|
|
49
|
+
}
|
|
50
|
+
getState() {
|
|
51
|
+
return {
|
|
52
|
+
...this.state,
|
|
53
|
+
nextSyncTime: this.isRunning ? this.state.lastSyncTime + this.config.interval * 60 * 1e3 : void 0
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
updateConfig(config) {
|
|
57
|
+
const wasRunning = this.isRunning;
|
|
58
|
+
if (wasRunning) {
|
|
59
|
+
this.stop();
|
|
60
|
+
}
|
|
61
|
+
this.config = { ...this.config, ...config };
|
|
62
|
+
if (wasRunning && this.config.enabled) {
|
|
63
|
+
this.start();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async forceSync() {
|
|
67
|
+
await this.performSync();
|
|
68
|
+
}
|
|
69
|
+
async performSync() {
|
|
70
|
+
if (!this.isRunning) return;
|
|
71
|
+
if (this.isInQuietHours()) {
|
|
72
|
+
this.onLog("DEBUG", "Skipping sync during quiet hours");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const { LinearAutoSyncService } = await import("../../integrations/linear/auto-sync.js");
|
|
77
|
+
const projectRoot = this.findProjectRoot();
|
|
78
|
+
if (!projectRoot) {
|
|
79
|
+
this.onLog("WARN", "No project root found for Linear sync");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const syncService = new LinearAutoSyncService(projectRoot, {
|
|
83
|
+
enabled: true,
|
|
84
|
+
interval: this.config.interval,
|
|
85
|
+
retryAttempts: this.config.retryAttempts,
|
|
86
|
+
retryDelay: this.config.retryDelay,
|
|
87
|
+
quietHours: this.config.quietHours
|
|
88
|
+
});
|
|
89
|
+
await syncService.forceSync();
|
|
90
|
+
syncService.stop();
|
|
91
|
+
this.state.syncCount++;
|
|
92
|
+
this.state.lastSyncTime = Date.now();
|
|
93
|
+
this.onLog("INFO", "Linear sync completed", {
|
|
94
|
+
syncCount: this.state.syncCount
|
|
95
|
+
});
|
|
96
|
+
} catch (err) {
|
|
97
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
98
|
+
this.state.errors.push(errorMsg);
|
|
99
|
+
this.onLog("ERROR", "Linear sync failed", { error: errorMsg });
|
|
100
|
+
if (this.state.errors.length > 10) {
|
|
101
|
+
this.state.errors = this.state.errors.slice(-10);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
isLinearConfigured() {
|
|
106
|
+
const homeDir = homedir();
|
|
107
|
+
const configPath = join(homeDir, ".stackmemory", "linear-auth.json");
|
|
108
|
+
return existsSync(configPath) || !!process.env["LINEAR_API_KEY"];
|
|
109
|
+
}
|
|
110
|
+
findProjectRoot() {
|
|
111
|
+
const cwd = process.cwd();
|
|
112
|
+
if (existsSync(join(cwd, ".stackmemory"))) {
|
|
113
|
+
return cwd;
|
|
114
|
+
}
|
|
115
|
+
const homeDir = homedir();
|
|
116
|
+
if (existsSync(join(homeDir, ".stackmemory"))) {
|
|
117
|
+
return homeDir;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
isInQuietHours() {
|
|
122
|
+
if (!this.config.quietHours) return false;
|
|
123
|
+
const now = /* @__PURE__ */ new Date();
|
|
124
|
+
const currentHour = now.getHours();
|
|
125
|
+
const { start, end } = this.config.quietHours;
|
|
126
|
+
if (start > end) {
|
|
127
|
+
return currentHour >= start || currentHour < end;
|
|
128
|
+
} else {
|
|
129
|
+
return currentHour >= start && currentHour < end;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
DaemonLinearService
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=linear-service.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/daemon/services/linear-service.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Linear Sync Service Wrapper\n * Wraps LinearAutoSyncService for daemon integration\n */\n\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport type { LinearServiceConfig } from '../daemon-config.js';\n\nexport interface LinearServiceState {\n lastSyncTime: number;\n syncCount: number;\n errors: string[];\n nextSyncTime?: number;\n}\n\nexport class DaemonLinearService {\n private config: LinearServiceConfig;\n private state: LinearServiceState;\n private intervalId?: NodeJS.Timeout;\n private isRunning = false;\n private onLog: (level: string, message: string, data?: unknown) => void;\n\n constructor(\n config: LinearServiceConfig,\n onLog: (level: string, message: string, data?: unknown) => void\n ) {\n this.config = config;\n this.onLog = onLog;\n this.state = {\n lastSyncTime: 0,\n syncCount: 0,\n errors: [],\n };\n }\n\n async start(): Promise<void> {\n if (this.isRunning || !this.config.enabled) {\n return;\n }\n\n // Check if Linear is configured\n if (!this.isLinearConfigured()) {\n this.onLog('WARN', 'Linear not configured, skipping linear service');\n return;\n }\n\n this.isRunning = true;\n const intervalMs = this.config.interval * 60 * 1000;\n\n this.onLog('INFO', 'Linear service started', {\n interval: this.config.interval,\n quietHours: this.config.quietHours,\n });\n\n // Initial sync\n await this.performSync();\n\n // Schedule periodic syncs\n this.intervalId = setInterval(async () => {\n await this.performSync();\n }, intervalMs);\n }\n\n stop(): void {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n this.isRunning = false;\n this.onLog('INFO', 'Linear service stopped');\n }\n\n getState(): LinearServiceState {\n return {\n ...this.state,\n nextSyncTime: this.isRunning\n ? this.state.lastSyncTime + this.config.interval * 60 * 1000\n : undefined,\n };\n }\n\n updateConfig(config: Partial<LinearServiceConfig>): void {\n const wasRunning = this.isRunning;\n if (wasRunning) {\n this.stop();\n }\n\n this.config = { ...this.config, ...config };\n\n if (wasRunning && this.config.enabled) {\n this.start();\n }\n }\n\n async forceSync(): Promise<void> {\n await this.performSync();\n }\n\n private async performSync(): Promise<void> {\n if (!this.isRunning) return;\n\n // Check quiet hours\n if (this.isInQuietHours()) {\n this.onLog('DEBUG', 'Skipping sync during quiet hours');\n return;\n }\n\n try {\n // Dynamically import LinearAutoSyncService to avoid loading if not needed\n const { LinearAutoSyncService } =\n await import('../../integrations/linear/auto-sync.js');\n\n const projectRoot = this.findProjectRoot();\n if (!projectRoot) {\n this.onLog('WARN', 'No project root found for Linear sync');\n return;\n }\n\n const syncService = new LinearAutoSyncService(projectRoot, {\n enabled: true,\n interval: this.config.interval,\n retryAttempts: this.config.retryAttempts,\n retryDelay: this.config.retryDelay,\n quietHours: this.config.quietHours,\n });\n\n await syncService.forceSync();\n syncService.stop();\n\n this.state.syncCount++;\n this.state.lastSyncTime = Date.now();\n\n this.onLog('INFO', 'Linear sync completed', {\n syncCount: this.state.syncCount,\n });\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n this.state.errors.push(errorMsg);\n this.onLog('ERROR', 'Linear sync failed', { error: errorMsg });\n\n // Keep only last 10 errors\n if (this.state.errors.length > 10) {\n this.state.errors = this.state.errors.slice(-10);\n }\n }\n }\n\n private isLinearConfigured(): boolean {\n const homeDir = homedir();\n const configPath = join(homeDir, '.stackmemory', 'linear-auth.json');\n return existsSync(configPath) || !!process.env['LINEAR_API_KEY'];\n }\n\n private findProjectRoot(): string | null {\n // Check common locations\n const cwd = process.cwd();\n if (existsSync(join(cwd, '.stackmemory'))) {\n return cwd;\n }\n\n // Check home directory\n const homeDir = homedir();\n if (existsSync(join(homeDir, '.stackmemory'))) {\n return homeDir;\n }\n\n return null;\n }\n\n private isInQuietHours(): boolean {\n if (!this.config.quietHours) return false;\n\n const now = new Date();\n const currentHour = now.getHours();\n const { start, end } = this.config.quietHours;\n\n if (start > end) {\n // Quiet hours span midnight (e.g., 22:00 - 07:00)\n return currentHour >= start || currentHour < end;\n } else {\n // Quiet hours within same day\n return currentHour >= start && currentHour < end;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|