@stackmemoryai/stackmemory 1.12.0 → 1.14.0
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/LICENSE +131 -64
- package/README.md +3 -1
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/codex-sm.js +51 -11
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/orchestrate.js +2 -0
- package/dist/src/cli/commands/orchestrator.js +3 -2
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/setup.js +1 -4
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +15 -10
- package/dist/src/cli/utils/real-cli-bin.js +72 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/token-estimator.js +24 -1
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +44 -0
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/database/sqlite-adapter.js +14 -1
- package/dist/src/core/models/model-router.js +33 -1
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/skill-packs/types.js +14 -1
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +45 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +28 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/{integrations/diffmem/index.js → features/web/client/hooks/use-socket.js} +6 -5
- package/dist/src/features/web/client/lib/utils.js +12 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +4 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +218 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +27 -52
- package/dist/src/integrations/mcp/server.js +122 -335
- package/dist/src/integrations/mcp/tool-alias-registry.js +0 -73
- package/dist/src/integrations/mcp/tool-definitions.js +111 -510
- package/dist/src/mcp/stackmemory-mcp-server.js +404 -379
- package/dist/src/orchestrators/multimodal/determinism.js +2 -1
- package/dist/src/orchestrators/multimodal/harness.js +2 -1
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +18 -9
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +0 -188
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -354
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -416
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1005
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile → features/operator}/types.js +0 -0
- /package/dist/src/{integrations/ralph/types.js → features/web/client/next-env.d.js} +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
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 Database from "better-sqlite3";
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
import { calculateBackoff } from "../../core/errors/recovery.js";
|
|
8
|
+
import { logger } from "../../core/monitoring/logger.js";
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
maxAttempts: 5,
|
|
11
|
+
initialDelay: 1e3,
|
|
12
|
+
maxDelay: 3e5,
|
|
13
|
+
backoffFactor: 2,
|
|
14
|
+
workerIntervalMs: 5e3
|
|
15
|
+
};
|
|
16
|
+
class WebhookDeliveryQueue {
|
|
17
|
+
db;
|
|
18
|
+
config;
|
|
19
|
+
workerTimer = null;
|
|
20
|
+
handler = null;
|
|
21
|
+
constructor(dbPath, config) {
|
|
22
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
23
|
+
this.db = new Database(dbPath);
|
|
24
|
+
this.db.pragma("journal_mode = WAL");
|
|
25
|
+
this.ensureSchema();
|
|
26
|
+
}
|
|
27
|
+
ensureSchema() {
|
|
28
|
+
this.db.exec(`
|
|
29
|
+
CREATE TABLE IF NOT EXISTS webhook_deliveries (
|
|
30
|
+
id TEXT PRIMARY KEY,
|
|
31
|
+
event_type TEXT NOT NULL,
|
|
32
|
+
payload TEXT NOT NULL,
|
|
33
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
34
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
max_attempts INTEGER NOT NULL DEFAULT 5,
|
|
36
|
+
next_retry_at INTEGER,
|
|
37
|
+
last_error TEXT,
|
|
38
|
+
created_at INTEGER NOT NULL,
|
|
39
|
+
updated_at INTEGER NOT NULL
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_status_retry
|
|
42
|
+
ON webhook_deliveries(status, next_retry_at);
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Set the handler function that processes webhook events.
|
|
47
|
+
*/
|
|
48
|
+
setHandler(fn) {
|
|
49
|
+
this.handler = fn;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Enqueue a webhook event for processing.
|
|
53
|
+
*/
|
|
54
|
+
enqueue(eventType, payload) {
|
|
55
|
+
const id = randomUUID();
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
this.db.prepare(
|
|
58
|
+
`INSERT INTO webhook_deliveries
|
|
59
|
+
(id, event_type, payload, status, attempts, max_attempts, next_retry_at, created_at, updated_at)
|
|
60
|
+
VALUES (?, ?, ?, 'pending', 0, ?, ?, ?, ?)`
|
|
61
|
+
).run(
|
|
62
|
+
id,
|
|
63
|
+
eventType,
|
|
64
|
+
JSON.stringify(payload),
|
|
65
|
+
this.config.maxAttempts,
|
|
66
|
+
now,
|
|
67
|
+
now,
|
|
68
|
+
now
|
|
69
|
+
);
|
|
70
|
+
return id;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Process the next eligible delivery. Returns true if a delivery was processed.
|
|
74
|
+
*/
|
|
75
|
+
async processNext() {
|
|
76
|
+
if (!this.handler) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const delivery = this.db.prepare(
|
|
81
|
+
`UPDATE webhook_deliveries
|
|
82
|
+
SET status = 'processing', updated_at = ?
|
|
83
|
+
WHERE id = (
|
|
84
|
+
SELECT id FROM webhook_deliveries
|
|
85
|
+
WHERE (status = 'pending' OR (status = 'failed' AND next_retry_at <= ?))
|
|
86
|
+
ORDER BY next_retry_at ASC, created_at ASC
|
|
87
|
+
LIMIT 1
|
|
88
|
+
)
|
|
89
|
+
RETURNING *`
|
|
90
|
+
).get(now, now);
|
|
91
|
+
if (!delivery) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const attempt = delivery.attempts + 1;
|
|
95
|
+
try {
|
|
96
|
+
const payload = JSON.parse(delivery.payload);
|
|
97
|
+
await this.handler(delivery.event_type, payload);
|
|
98
|
+
this.db.prepare(
|
|
99
|
+
`UPDATE webhook_deliveries
|
|
100
|
+
SET status = 'completed', attempts = ?, last_error = NULL, updated_at = ?
|
|
101
|
+
WHERE id = ?`
|
|
102
|
+
).run(attempt, Date.now(), delivery.id);
|
|
103
|
+
logger.info(
|
|
104
|
+
`Webhook delivery ${delivery.id} completed on attempt ${attempt}`
|
|
105
|
+
);
|
|
106
|
+
return true;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
109
|
+
if (attempt >= delivery.max_attempts) {
|
|
110
|
+
this.db.prepare(
|
|
111
|
+
`UPDATE webhook_deliveries
|
|
112
|
+
SET status = 'dead', attempts = ?, last_error = ?, updated_at = ?
|
|
113
|
+
WHERE id = ?`
|
|
114
|
+
).run(attempt, errorMsg, Date.now(), delivery.id);
|
|
115
|
+
logger.error(
|
|
116
|
+
`Webhook delivery ${delivery.id} dead after ${attempt} attempts: ${errorMsg}`
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
const delay = calculateBackoff(
|
|
120
|
+
attempt,
|
|
121
|
+
this.config.initialDelay,
|
|
122
|
+
this.config.maxDelay,
|
|
123
|
+
this.config.backoffFactor
|
|
124
|
+
);
|
|
125
|
+
const nextRetry = Date.now() + delay;
|
|
126
|
+
this.db.prepare(
|
|
127
|
+
`UPDATE webhook_deliveries
|
|
128
|
+
SET status = 'failed', attempts = ?, last_error = ?, next_retry_at = ?, updated_at = ?
|
|
129
|
+
WHERE id = ?`
|
|
130
|
+
).run(attempt, errorMsg, nextRetry, Date.now(), delivery.id);
|
|
131
|
+
logger.warn(
|
|
132
|
+
`Webhook delivery ${delivery.id} failed (attempt ${attempt}/${delivery.max_attempts}), retry in ${delay}ms`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Start the background worker that polls for deliveries.
|
|
140
|
+
*/
|
|
141
|
+
startWorker() {
|
|
142
|
+
if (this.workerTimer) return;
|
|
143
|
+
this.workerTimer = setInterval(async () => {
|
|
144
|
+
try {
|
|
145
|
+
while (await this.processNext()) {
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
logger.error("Webhook retry worker error", error);
|
|
149
|
+
}
|
|
150
|
+
}, this.config.workerIntervalMs);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Stop the background worker.
|
|
154
|
+
*/
|
|
155
|
+
stopWorker() {
|
|
156
|
+
if (this.workerTimer) {
|
|
157
|
+
clearInterval(this.workerTimer);
|
|
158
|
+
this.workerTimer = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get delivery counts by status.
|
|
163
|
+
*/
|
|
164
|
+
getStats() {
|
|
165
|
+
const rows = this.db.prepare(
|
|
166
|
+
`SELECT status, COUNT(*) as count FROM webhook_deliveries GROUP BY status`
|
|
167
|
+
).all();
|
|
168
|
+
const stats = {
|
|
169
|
+
pending: 0,
|
|
170
|
+
processing: 0,
|
|
171
|
+
completed: 0,
|
|
172
|
+
failed: 0,
|
|
173
|
+
dead: 0
|
|
174
|
+
};
|
|
175
|
+
for (const row of rows) {
|
|
176
|
+
stats[row.status] = row.count;
|
|
177
|
+
}
|
|
178
|
+
return stats;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get a single delivery by ID.
|
|
182
|
+
*/
|
|
183
|
+
getDelivery(id) {
|
|
184
|
+
return this.db.prepare(`SELECT * FROM webhook_deliveries WHERE id = ?`).get(id);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Close the database connection and stop the worker.
|
|
188
|
+
*/
|
|
189
|
+
close() {
|
|
190
|
+
this.stopWorker();
|
|
191
|
+
this.db.close();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export {
|
|
195
|
+
WebhookDeliveryQueue
|
|
196
|
+
};
|
|
@@ -8,7 +8,10 @@ import crypto from "crypto";
|
|
|
8
8
|
import { LinearSyncService } from "./sync-service.js";
|
|
9
9
|
import { IntegrationError, ErrorCode } from "../../core/errors/index.js";
|
|
10
10
|
import { logger } from "../../core/monitoring/logger.js";
|
|
11
|
+
import { WebhookDeliveryQueue } from "./webhook-retry.js";
|
|
11
12
|
import chalk from "chalk";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
12
15
|
function _getEnv(key, defaultValue) {
|
|
13
16
|
const value = process.env[key];
|
|
14
17
|
if (value === void 0) {
|
|
@@ -29,21 +32,28 @@ class LinearWebhookServer {
|
|
|
29
32
|
// Using singleton logger from monitoring
|
|
30
33
|
syncService;
|
|
31
34
|
config;
|
|
32
|
-
|
|
33
|
-
isProcessing = false;
|
|
35
|
+
deliveryQueue;
|
|
34
36
|
constructor(config) {
|
|
35
37
|
this.app = express();
|
|
36
38
|
this.syncService = new LinearSyncService();
|
|
39
|
+
const dbPath = config?.dbPath || join(homedir(), ".stackmemory", "webhook-deliveries.db");
|
|
37
40
|
this.config = {
|
|
38
41
|
port: config?.port || parseInt(process.env["WEBHOOK_PORT"] || "3456"),
|
|
39
42
|
host: config?.host || process.env["WEBHOOK_HOST"] || "localhost",
|
|
40
43
|
webhookSecret: config?.webhookSecret || process.env["LINEAR_WEBHOOK_SECRET"],
|
|
41
44
|
maxPayloadSize: config?.maxPayloadSize || "10mb",
|
|
45
|
+
dbPath,
|
|
42
46
|
rateLimit: {
|
|
43
47
|
windowMs: config?.rateLimit?.windowMs || 6e4,
|
|
44
48
|
max: config?.rateLimit?.max || 100
|
|
45
49
|
}
|
|
46
50
|
};
|
|
51
|
+
this.deliveryQueue = new WebhookDeliveryQueue(dbPath);
|
|
52
|
+
this.deliveryQueue.setHandler(
|
|
53
|
+
async (eventType, payload) => {
|
|
54
|
+
await this.handleWebhookEvent(payload);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
47
57
|
this.setupMiddleware();
|
|
48
58
|
this.setupRoutes();
|
|
49
59
|
}
|
|
@@ -61,12 +71,12 @@ class LinearWebhookServer {
|
|
|
61
71
|
}
|
|
62
72
|
setupRoutes() {
|
|
63
73
|
this.app.get("/health", (req, res) => {
|
|
74
|
+
const stats = this.deliveryQueue.getStats();
|
|
64
75
|
res.json({
|
|
65
76
|
status: "healthy",
|
|
66
77
|
service: "linear-webhook",
|
|
67
78
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
68
|
-
|
|
69
|
-
processing: this.isProcessing
|
|
79
|
+
deliveries: stats
|
|
70
80
|
});
|
|
71
81
|
});
|
|
72
82
|
this.app.post("/webhook/linear", async (req, res) => {
|
|
@@ -77,11 +87,10 @@ class LinearWebhookServer {
|
|
|
77
87
|
}
|
|
78
88
|
const payload = JSON.parse(req.body.toString());
|
|
79
89
|
logger.info(`Received webhook: ${payload.type} - ${payload.action}`);
|
|
80
|
-
this.
|
|
81
|
-
this.processQueue();
|
|
90
|
+
const deliveryId = this.deliveryQueue.enqueue(payload.type, payload);
|
|
82
91
|
return res.status(200).json({
|
|
83
92
|
status: "accepted",
|
|
84
|
-
|
|
93
|
+
deliveryId
|
|
85
94
|
});
|
|
86
95
|
} catch (error) {
|
|
87
96
|
logger.error("Webhook processing error:", error);
|
|
@@ -104,21 +113,6 @@ class LinearWebhookServer {
|
|
|
104
113
|
const hash = crypto.createHmac("sha256", this.config.webhookSecret).update(req.body).digest("hex");
|
|
105
114
|
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(hash));
|
|
106
115
|
}
|
|
107
|
-
async processQueue() {
|
|
108
|
-
if (this.isProcessing || this.eventQueue.length === 0) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
this.isProcessing = true;
|
|
112
|
-
while (this.eventQueue.length > 0) {
|
|
113
|
-
const event = this.eventQueue.shift();
|
|
114
|
-
try {
|
|
115
|
-
await this.handleWebhookEvent(event);
|
|
116
|
-
} catch (error) {
|
|
117
|
-
logger.error(`Failed to process event: ${event.type}`, error);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
this.isProcessing = false;
|
|
121
|
-
}
|
|
122
116
|
async handleWebhookEvent(payload) {
|
|
123
117
|
const { type, action, data } = payload;
|
|
124
118
|
switch (type) {
|
|
@@ -166,6 +160,7 @@ class LinearWebhookServer {
|
|
|
166
160
|
this.config.port,
|
|
167
161
|
this.config.host,
|
|
168
162
|
() => {
|
|
163
|
+
this.deliveryQueue.startWorker();
|
|
169
164
|
console.log(
|
|
170
165
|
chalk.green("\u2713") + chalk.bold(" Linear Webhook Server Started")
|
|
171
166
|
);
|
|
@@ -188,6 +183,7 @@ class LinearWebhookServer {
|
|
|
188
183
|
});
|
|
189
184
|
}
|
|
190
185
|
async stop() {
|
|
186
|
+
this.deliveryQueue.close();
|
|
191
187
|
return new Promise((resolve) => {
|
|
192
188
|
if (this.server) {
|
|
193
189
|
this.server.close(() => {
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
const VALID_SYNC_TABLES = /* @__PURE__ */ new Set([
|
|
6
|
+
"frames",
|
|
7
|
+
"events",
|
|
8
|
+
"anchors",
|
|
9
|
+
"trace_events",
|
|
10
|
+
"entity_states"
|
|
11
|
+
]);
|
|
12
|
+
class CloudSyncHandlers {
|
|
13
|
+
syncManager;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.syncManager = deps.syncManager;
|
|
16
|
+
}
|
|
17
|
+
ensureManager() {
|
|
18
|
+
if (!this.syncManager) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"Cloud sync not configured. Run `stackmemory login` to connect to Provenant."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return this.syncManager;
|
|
24
|
+
}
|
|
25
|
+
async handlePush(args) {
|
|
26
|
+
const manager = this.ensureManager();
|
|
27
|
+
const result = await manager.performPush("manual", args.force);
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: JSON.stringify(
|
|
33
|
+
{
|
|
34
|
+
success: result.success,
|
|
35
|
+
pushed: result.pushed,
|
|
36
|
+
rejected: result.rejected,
|
|
37
|
+
conflicts: result.conflicts,
|
|
38
|
+
error: result.error
|
|
39
|
+
},
|
|
40
|
+
null,
|
|
41
|
+
2
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async handlePull(args) {
|
|
48
|
+
const manager = this.ensureManager();
|
|
49
|
+
const tables = args.tables?.filter((t) => VALID_SYNC_TABLES.has(t));
|
|
50
|
+
const result = await manager.performPull("manual", tables);
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: JSON.stringify(
|
|
56
|
+
{
|
|
57
|
+
success: result.success,
|
|
58
|
+
pulled: result.pulled,
|
|
59
|
+
applied: result.applied,
|
|
60
|
+
conflicts: result.conflicts,
|
|
61
|
+
error: result.error
|
|
62
|
+
},
|
|
63
|
+
null,
|
|
64
|
+
2
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async handleStatus() {
|
|
71
|
+
if (!this.syncManager) {
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: JSON.stringify(
|
|
77
|
+
{
|
|
78
|
+
connected: false,
|
|
79
|
+
message: "Cloud sync not configured. Run `stackmemory login` to connect."
|
|
80
|
+
},
|
|
81
|
+
null,
|
|
82
|
+
2
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const status = this.syncManager.getStatus();
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: JSON.stringify(status, null, 2)
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
CloudSyncHandlers
|
|
101
|
+
};
|
|
@@ -15,15 +15,12 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
DiscoveryHandlers
|
|
17
17
|
} from "./discovery-handlers.js";
|
|
18
|
-
import {
|
|
19
|
-
ProviderHandlers
|
|
20
|
-
} from "./provider-handlers.js";
|
|
21
18
|
import {
|
|
22
19
|
ProvenantHandlers
|
|
23
20
|
} from "./provenant-handlers.js";
|
|
24
21
|
import {
|
|
25
|
-
|
|
26
|
-
} from "./
|
|
22
|
+
CloudSyncHandlers
|
|
23
|
+
} from "./cloud-sync-handlers.js";
|
|
27
24
|
import {
|
|
28
25
|
ContextHandlers as ContextHandlers2
|
|
29
26
|
} from "./context-handlers.js";
|
|
@@ -32,9 +29,8 @@ import {
|
|
|
32
29
|
LinearHandlers as LinearHandlers2
|
|
33
30
|
} from "./linear-handlers.js";
|
|
34
31
|
import { TraceHandlers as TraceHandlers2 } from "./trace-handlers.js";
|
|
35
|
-
import { ProviderHandlers as ProviderHandlers2 } from "./provider-handlers.js";
|
|
36
32
|
import { ProvenantHandlers as ProvenantHandlers2 } from "./provenant-handlers.js";
|
|
37
|
-
import {
|
|
33
|
+
import { CloudSyncHandlers as CloudSyncHandlers2 } from "./cloud-sync-handlers.js";
|
|
38
34
|
import {
|
|
39
35
|
resolveToolAlias
|
|
40
36
|
} from "../tool-alias-registry.js";
|
|
@@ -43,9 +39,8 @@ class MCPHandlerFactory {
|
|
|
43
39
|
taskHandlers;
|
|
44
40
|
linearHandlers;
|
|
45
41
|
traceHandlers;
|
|
46
|
-
providerHandlers;
|
|
47
42
|
provenantHandlers;
|
|
48
|
-
|
|
43
|
+
cloudSyncHandlers;
|
|
49
44
|
constructor(deps) {
|
|
50
45
|
this.contextHandlers = new ContextHandlers2({
|
|
51
46
|
frameManager: deps.frameManager
|
|
@@ -63,13 +58,21 @@ class MCPHandlerFactory {
|
|
|
63
58
|
traceDetector: deps.traceDetector,
|
|
64
59
|
browserMCP: deps.browserMCP
|
|
65
60
|
});
|
|
66
|
-
this.providerHandlers = new ProviderHandlers2();
|
|
67
61
|
if (deps.projectDir) {
|
|
68
62
|
this.provenantHandlers = new ProvenantHandlers2({
|
|
69
63
|
projectDir: deps.projectDir
|
|
70
64
|
});
|
|
71
65
|
}
|
|
72
|
-
this.
|
|
66
|
+
this.cloudSyncHandlers = new CloudSyncHandlers2({
|
|
67
|
+
syncManager: null
|
|
68
|
+
// Initialized lazily when cloud sync is configured
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Set the cloud sync manager (called after config/auth is loaded)
|
|
73
|
+
*/
|
|
74
|
+
setCloudSyncManager(manager) {
|
|
75
|
+
this.cloudSyncHandlers = new CloudSyncHandlers2({ syncManager: manager });
|
|
73
76
|
}
|
|
74
77
|
/**
|
|
75
78
|
* Get handler for a specific tool.
|
|
@@ -133,19 +136,6 @@ class MCPHandlerFactory {
|
|
|
133
136
|
this.traceHandlers
|
|
134
137
|
);
|
|
135
138
|
// Provider handlers
|
|
136
|
-
case "delegate_to_model":
|
|
137
|
-
return this.providerHandlers.handleDelegateToModel.bind(
|
|
138
|
-
this.providerHandlers
|
|
139
|
-
);
|
|
140
|
-
case "batch_submit":
|
|
141
|
-
return this.providerHandlers.handleBatchSubmit.bind(
|
|
142
|
-
this.providerHandlers
|
|
143
|
-
);
|
|
144
|
-
case "batch_check":
|
|
145
|
-
return this.providerHandlers.handleBatchCheck.bind(
|
|
146
|
-
this.providerHandlers
|
|
147
|
-
);
|
|
148
|
-
// Provenant decision graph handlers
|
|
149
139
|
case "provenant_search":
|
|
150
140
|
if (!this.provenantHandlers)
|
|
151
141
|
throw new Error("Provenant tools require projectDir");
|
|
@@ -171,22 +161,13 @@ class MCPHandlerFactory {
|
|
|
171
161
|
this.provenantHandlers
|
|
172
162
|
);
|
|
173
163
|
// Cross-project search handlers
|
|
174
|
-
case "
|
|
175
|
-
return this.
|
|
176
|
-
|
|
177
|
-
);
|
|
178
|
-
case "
|
|
179
|
-
return this.
|
|
180
|
-
|
|
181
|
-
);
|
|
182
|
-
case "sm_cross_register":
|
|
183
|
-
return this.crossSearchHandlers.handleCrossRegister.bind(
|
|
184
|
-
this.crossSearchHandlers
|
|
185
|
-
);
|
|
186
|
-
case "sm_cross_list":
|
|
187
|
-
return this.crossSearchHandlers.handleCrossList.bind(
|
|
188
|
-
this.crossSearchHandlers
|
|
189
|
-
);
|
|
164
|
+
case "cloud_sync_push":
|
|
165
|
+
return this.cloudSyncHandlers.handlePush.bind(this.cloudSyncHandlers);
|
|
166
|
+
case "cloud_sync_pull":
|
|
167
|
+
return this.cloudSyncHandlers.handlePull.bind(this.cloudSyncHandlers);
|
|
168
|
+
case "cloud_sync_status":
|
|
169
|
+
return this.cloudSyncHandlers.handleStatus.bind(this.cloudSyncHandlers);
|
|
170
|
+
// Workflow handlers
|
|
190
171
|
default:
|
|
191
172
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
192
173
|
}
|
|
@@ -219,21 +200,16 @@ class MCPHandlerFactory {
|
|
|
219
200
|
"take_screenshot",
|
|
220
201
|
"execute_script",
|
|
221
202
|
"stop_browser_debug",
|
|
222
|
-
//
|
|
223
|
-
"delegate_to_model",
|
|
224
|
-
"batch_submit",
|
|
225
|
-
"batch_check",
|
|
226
|
-
// Provenant decision graph tools
|
|
203
|
+
// Provenant tools
|
|
227
204
|
"provenant_search",
|
|
228
205
|
"provenant_log",
|
|
229
206
|
"provenant_status",
|
|
230
207
|
"provenant_contradictions",
|
|
231
208
|
"provenant_resolve",
|
|
232
|
-
//
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
"sm_cross_list"
|
|
209
|
+
// Cloud sync tools
|
|
210
|
+
"cloud_sync_push",
|
|
211
|
+
"cloud_sync_pull",
|
|
212
|
+
"cloud_sync_status"
|
|
237
213
|
];
|
|
238
214
|
}
|
|
239
215
|
/**
|
|
@@ -245,13 +221,12 @@ class MCPHandlerFactory {
|
|
|
245
221
|
}
|
|
246
222
|
}
|
|
247
223
|
export {
|
|
224
|
+
CloudSyncHandlers,
|
|
248
225
|
ContextHandlers,
|
|
249
|
-
CrossSearchHandlers,
|
|
250
226
|
DiscoveryHandlers,
|
|
251
227
|
LinearHandlers,
|
|
252
228
|
MCPHandlerFactory,
|
|
253
229
|
ProvenantHandlers,
|
|
254
|
-
ProviderHandlers,
|
|
255
230
|
TaskHandlers,
|
|
256
231
|
TraceHandlers
|
|
257
232
|
};
|