forge-remote 0.1.18 → 0.1.20
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/package.json +1 -1
- package/src/session-manager.js +30 -8
- package/src/webhook-server.js +15 -0
package/package.json
CHANGED
package/src/session-manager.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Created by Daniel Wendel, CEO/Founder of Iron Forge Apps
|
|
4
4
|
|
|
5
5
|
import { getDb, FieldValue } from "./firebase.js";
|
|
6
|
-
import { readdir, realpath } from "node:fs/promises";
|
|
6
|
+
import { readdir, realpath, mkdir } from "node:fs/promises";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { spawn, execSync } from "child_process";
|
|
@@ -490,8 +490,14 @@ export async function startNewSession(desktopId, payload) {
|
|
|
490
490
|
);
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
const {
|
|
494
|
-
|
|
493
|
+
const {
|
|
494
|
+
prompt,
|
|
495
|
+
projectPath,
|
|
496
|
+
model,
|
|
497
|
+
webhookMeta,
|
|
498
|
+
allowedTools,
|
|
499
|
+
createIfMissing,
|
|
500
|
+
} = payload || {};
|
|
495
501
|
const db = getDb();
|
|
496
502
|
const resolvedModel = model || "sonnet";
|
|
497
503
|
const resolvedPath = projectPath || process.cwd();
|
|
@@ -503,7 +509,12 @@ export async function startNewSession(desktopId, payload) {
|
|
|
503
509
|
try {
|
|
504
510
|
await realpath(resolvedPath);
|
|
505
511
|
} catch {
|
|
506
|
-
|
|
512
|
+
if (createIfMissing) {
|
|
513
|
+
log.info(`Creating project directory: ${resolvedPath}`);
|
|
514
|
+
await mkdir(resolvedPath, { recursive: true });
|
|
515
|
+
} else {
|
|
516
|
+
throw new Error(`projectPath does not exist: ${resolvedPath}`);
|
|
517
|
+
}
|
|
507
518
|
}
|
|
508
519
|
|
|
509
520
|
const projectName = resolvedPath.split("/").pop();
|
|
@@ -726,7 +737,12 @@ async function runClaudeProcess(sessionId, prompt) {
|
|
|
726
737
|
}
|
|
727
738
|
session.isFirstPrompt = false;
|
|
728
739
|
|
|
729
|
-
|
|
740
|
+
// IMPORTANT: When --allowedTools is used, it consumes all remaining
|
|
741
|
+
// positional args. So we must pass the prompt via stdin instead.
|
|
742
|
+
const promptViaStdin = !!session.webhookMeta;
|
|
743
|
+
if (!promptViaStdin) {
|
|
744
|
+
args.push(prompt);
|
|
745
|
+
}
|
|
730
746
|
|
|
731
747
|
log.session(sessionId, `Spawning: ${claudeBinary} ${args.join(" ")}`);
|
|
732
748
|
|
|
@@ -741,9 +757,15 @@ async function runClaudeProcess(sessionId, prompt) {
|
|
|
741
757
|
|
|
742
758
|
session.process = claudeProcess;
|
|
743
759
|
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
|
|
760
|
+
// When --allowedTools is used (webhook sessions), the prompt is sent via
|
|
761
|
+
// stdin because --allowedTools is variadic and would consume the prompt
|
|
762
|
+
// if passed as a positional arg. Otherwise, close stdin immediately.
|
|
763
|
+
if (promptViaStdin) {
|
|
764
|
+
claudeProcess.stdin.write(prompt);
|
|
765
|
+
claudeProcess.stdin.end();
|
|
766
|
+
} else {
|
|
767
|
+
claudeProcess.stdin.end();
|
|
768
|
+
}
|
|
747
769
|
|
|
748
770
|
log.session(sessionId, `Process started — PID ${claudeProcess.pid}`);
|
|
749
771
|
|
package/src/webhook-server.js
CHANGED
|
@@ -361,6 +361,21 @@ async function handleWebhookPost(req, res, webhookId, sourceIp) {
|
|
|
361
361
|
});
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
// When subscribed to both message and app_mention events, Slack sends
|
|
365
|
+
// separate events for the same user message. Deduplicate by using the
|
|
366
|
+
// Slack message timestamp (ts) which is identical across both events.
|
|
367
|
+
if (event.ts) {
|
|
368
|
+
const tsKey = `slack-ts:${event.channel}:${event.ts}`;
|
|
369
|
+
if (recentDeliveryIds.has(tsKey)) {
|
|
370
|
+
log.info(`Duplicate Slack message ts ignored: ${event.ts}`);
|
|
371
|
+
return sendJson(res, 200, {
|
|
372
|
+
status: "duplicate",
|
|
373
|
+
reason: "same message ts",
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
addDeliveryId(tsKey);
|
|
377
|
+
}
|
|
378
|
+
|
|
364
379
|
// Flatten the Slack event into the payload for template rendering.
|
|
365
380
|
// This lets templates use {{text}}, {{user}}, {{channel}} directly.
|
|
366
381
|
payload = { ...payload, ...event };
|