@opengsd/gsd-pi 1.0.2-dev.50223bc → 1.0.2-dev.5961fbf
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/dist/resource-loader.d.ts +5 -0
- package/dist/resource-loader.js +24 -8
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +19 -0
- package/dist/resources/extensions/gsd/auto/phases.js +1 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
- package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/1834.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/package.json +0 -1
- package/dist/worktree-cli.d.ts +0 -2
- package/dist/worktree-cli.js +21 -9
- package/package.json +9 -4
- package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
- package/packages/cloud-mcp-gateway/package.json +5 -4
- package/packages/contracts/package.json +2 -2
- package/packages/daemon/bin/gsd-daemon.js +14 -0
- package/packages/daemon/bin/gsd-mcp-runtime.js +14 -0
- package/packages/daemon/bin/gsd-mcp.js +14 -0
- package/packages/daemon/dist/channel-manager.d.ts +53 -0
- package/packages/daemon/dist/channel-manager.d.ts.map +1 -0
- package/packages/daemon/dist/channel-manager.js +167 -0
- package/packages/daemon/dist/channel-manager.js.map +1 -0
- package/packages/daemon/dist/cli.d.ts +3 -0
- package/packages/daemon/dist/cli.d.ts.map +1 -0
- package/packages/daemon/dist/cli.js +94 -0
- package/packages/daemon/dist/cli.js.map +1 -0
- package/packages/daemon/dist/cloud-cli.d.ts +7 -0
- package/packages/daemon/dist/cloud-cli.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-cli.js +96 -0
- package/packages/daemon/dist/cloud-cli.js.map +1 -0
- package/packages/daemon/dist/cloud-config.d.ts +18 -0
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-config.js +209 -0
- package/packages/daemon/dist/cloud-config.js.map +1 -0
- package/packages/daemon/dist/cloud-config.test.d.ts +2 -0
- package/packages/daemon/dist/cloud-config.test.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-config.test.js +132 -0
- package/packages/daemon/dist/cloud-config.test.js.map +1 -0
- package/packages/daemon/dist/cloud-runtime.d.ts +26 -0
- package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-runtime.js +180 -0
- package/packages/daemon/dist/cloud-runtime.js.map +1 -0
- package/packages/daemon/dist/cloud-runtime.test.d.ts +2 -0
- package/packages/daemon/dist/cloud-runtime.test.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-runtime.test.js +28 -0
- package/packages/daemon/dist/cloud-runtime.test.js.map +1 -0
- package/packages/daemon/dist/cloud-token.d.ts +3 -0
- package/packages/daemon/dist/cloud-token.d.ts.map +1 -0
- package/packages/daemon/dist/cloud-token.js +37 -0
- package/packages/daemon/dist/cloud-token.js.map +1 -0
- package/packages/daemon/dist/commands.d.ts +25 -0
- package/packages/daemon/dist/commands.d.ts.map +1 -0
- package/packages/daemon/dist/commands.js +81 -0
- package/packages/daemon/dist/commands.js.map +1 -0
- package/packages/daemon/dist/config.d.ts +17 -0
- package/packages/daemon/dist/config.d.ts.map +1 -0
- package/packages/daemon/dist/config.js +146 -0
- package/packages/daemon/dist/config.js.map +1 -0
- package/packages/daemon/dist/daemon.d.ts +38 -0
- package/packages/daemon/dist/daemon.d.ts.map +1 -0
- package/packages/daemon/dist/daemon.js +194 -0
- package/packages/daemon/dist/daemon.js.map +1 -0
- package/packages/daemon/dist/daemon.test.d.ts +2 -0
- package/packages/daemon/dist/daemon.test.d.ts.map +1 -0
- package/packages/daemon/dist/daemon.test.js +692 -0
- package/packages/daemon/dist/daemon.test.js.map +1 -0
- package/packages/daemon/dist/discord-bot.d.ts +70 -0
- package/packages/daemon/dist/discord-bot.d.ts.map +1 -0
- package/packages/daemon/dist/discord-bot.js +433 -0
- package/packages/daemon/dist/discord-bot.js.map +1 -0
- package/packages/daemon/dist/discord-bot.test.d.ts +2 -0
- package/packages/daemon/dist/discord-bot.test.d.ts.map +1 -0
- package/packages/daemon/dist/discord-bot.test.js +667 -0
- package/packages/daemon/dist/discord-bot.test.js.map +1 -0
- package/packages/daemon/dist/event-bridge.d.ts +72 -0
- package/packages/daemon/dist/event-bridge.d.ts.map +1 -0
- package/packages/daemon/dist/event-bridge.js +366 -0
- package/packages/daemon/dist/event-bridge.js.map +1 -0
- package/packages/daemon/dist/event-bridge.test.d.ts +9 -0
- package/packages/daemon/dist/event-bridge.test.d.ts.map +1 -0
- package/packages/daemon/dist/event-bridge.test.js +528 -0
- package/packages/daemon/dist/event-bridge.test.js.map +1 -0
- package/packages/daemon/dist/event-formatter.d.ts +34 -0
- package/packages/daemon/dist/event-formatter.d.ts.map +1 -0
- package/packages/daemon/dist/event-formatter.js +355 -0
- package/packages/daemon/dist/event-formatter.js.map +1 -0
- package/packages/daemon/dist/event-formatter.test.d.ts +2 -0
- package/packages/daemon/dist/event-formatter.test.d.ts.map +1 -0
- package/packages/daemon/dist/event-formatter.test.js +333 -0
- package/packages/daemon/dist/event-formatter.test.js.map +1 -0
- package/packages/daemon/dist/index.d.ts +25 -0
- package/packages/daemon/dist/index.d.ts.map +1 -0
- package/packages/daemon/dist/index.js +17 -0
- package/packages/daemon/dist/index.js.map +1 -0
- package/packages/daemon/dist/launchd.d.ts +49 -0
- package/packages/daemon/dist/launchd.d.ts.map +1 -0
- package/packages/daemon/dist/launchd.js +188 -0
- package/packages/daemon/dist/launchd.js.map +1 -0
- package/packages/daemon/dist/launchd.test.d.ts +2 -0
- package/packages/daemon/dist/launchd.test.d.ts.map +1 -0
- package/packages/daemon/dist/launchd.test.js +296 -0
- package/packages/daemon/dist/launchd.test.js.map +1 -0
- package/packages/daemon/dist/local-tool-executor.d.ts +22 -0
- package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -0
- package/packages/daemon/dist/local-tool-executor.js +307 -0
- package/packages/daemon/dist/local-tool-executor.js.map +1 -0
- package/packages/daemon/dist/local-tool-executor.test.d.ts +2 -0
- package/packages/daemon/dist/local-tool-executor.test.d.ts.map +1 -0
- package/packages/daemon/dist/local-tool-executor.test.js +111 -0
- package/packages/daemon/dist/local-tool-executor.test.js.map +1 -0
- package/packages/daemon/dist/logger.d.ts +25 -0
- package/packages/daemon/dist/logger.d.ts.map +1 -0
- package/packages/daemon/dist/logger.js +72 -0
- package/packages/daemon/dist/logger.js.map +1 -0
- package/packages/daemon/dist/mcp-cli.d.ts +3 -0
- package/packages/daemon/dist/mcp-cli.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-cli.js +8 -0
- package/packages/daemon/dist/mcp-cli.js.map +1 -0
- package/packages/daemon/dist/mcp-cli.test.d.ts +2 -0
- package/packages/daemon/dist/mcp-cli.test.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-cli.test.js +13 -0
- package/packages/daemon/dist/mcp-cli.test.js.map +1 -0
- package/packages/daemon/dist/mcp-runtime-cli.d.ts +3 -0
- package/packages/daemon/dist/mcp-runtime-cli.d.ts.map +1 -0
- package/packages/daemon/dist/mcp-runtime-cli.js +8 -0
- package/packages/daemon/dist/mcp-runtime-cli.js.map +1 -0
- package/packages/daemon/dist/message-batcher.d.ts +78 -0
- package/packages/daemon/dist/message-batcher.d.ts.map +1 -0
- package/packages/daemon/dist/message-batcher.js +173 -0
- package/packages/daemon/dist/message-batcher.js.map +1 -0
- package/packages/daemon/dist/message-batcher.test.d.ts +2 -0
- package/packages/daemon/dist/message-batcher.test.d.ts.map +1 -0
- package/packages/daemon/dist/message-batcher.test.js +242 -0
- package/packages/daemon/dist/message-batcher.test.js.map +1 -0
- package/packages/daemon/dist/orchestrator.d.ts +98 -0
- package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator.js +359 -0
- package/packages/daemon/dist/orchestrator.js.map +1 -0
- package/packages/daemon/dist/orchestrator.test.d.ts +8 -0
- package/packages/daemon/dist/orchestrator.test.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator.test.js +425 -0
- package/packages/daemon/dist/orchestrator.test.js.map +1 -0
- package/packages/daemon/dist/project-scanner.d.ts +18 -0
- package/packages/daemon/dist/project-scanner.d.ts.map +1 -0
- package/packages/daemon/dist/project-scanner.js +90 -0
- package/packages/daemon/dist/project-scanner.js.map +1 -0
- package/packages/daemon/dist/project-scanner.test.d.ts +5 -0
- package/packages/daemon/dist/project-scanner.test.d.ts.map +1 -0
- package/packages/daemon/dist/project-scanner.test.js +183 -0
- package/packages/daemon/dist/project-scanner.test.js.map +1 -0
- package/packages/daemon/dist/session-manager.d.ts +70 -0
- package/packages/daemon/dist/session-manager.d.ts.map +1 -0
- package/packages/daemon/dist/session-manager.js +358 -0
- package/packages/daemon/dist/session-manager.js.map +1 -0
- package/packages/daemon/dist/session-manager.test.d.ts +9 -0
- package/packages/daemon/dist/session-manager.test.d.ts.map +1 -0
- package/packages/daemon/dist/session-manager.test.js +616 -0
- package/packages/daemon/dist/session-manager.test.js.map +1 -0
- package/packages/daemon/dist/types.d.ts +133 -0
- package/packages/daemon/dist/types.d.ts.map +1 -0
- package/packages/daemon/dist/types.js +8 -0
- package/packages/daemon/dist/types.js.map +1 -0
- package/packages/daemon/dist/verbosity.d.ts +27 -0
- package/packages/daemon/dist/verbosity.d.ts.map +1 -0
- package/packages/daemon/dist/verbosity.js +86 -0
- package/packages/daemon/dist/verbosity.js.map +1 -0
- package/packages/daemon/dist/verbosity.test.d.ts +2 -0
- package/packages/daemon/dist/verbosity.test.d.ts.map +1 -0
- package/packages/daemon/dist/verbosity.test.js +136 -0
- package/packages/daemon/dist/verbosity.test.js.map +1 -0
- package/packages/daemon/package.json +9 -8
- package/packages/gsd-agent-core/package.json +6 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +8 -8
- package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
- package/packages/mcp-server/package.json +6 -5
- package/packages/native/package.json +3 -3
- package/packages/pi-agent-core/package.json +4 -4
- package/packages/pi-ai/bin/pi-ai.js +14 -0
- package/packages/pi-ai/dist/models.generated.d.ts +0 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +18 -35
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +5 -4
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/package.json +9 -9
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +3 -3
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +4 -4
- package/scripts/install/deps.js +10 -0
- package/src/resources/extensions/gsd/auto/loop.ts +22 -0
- package/src/resources/extensions/gsd/auto/phases.ts +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
- package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
- package/dist/tsconfig.extensions.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{JP7xjsa5zSaO76XhE-mFJ → spUYLkQXoHJyxYOMH9VQy}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { SessionManager } from './session-manager.js';
|
|
2
|
+
import { scanForProjects } from './project-scanner.js';
|
|
3
|
+
import { DiscordBot, validateDiscordConfig } from './discord-bot.js';
|
|
4
|
+
import { EventBridge } from './event-bridge.js';
|
|
5
|
+
import { Orchestrator } from './orchestrator.js';
|
|
6
|
+
import { CloudRuntime } from './cloud-runtime.js';
|
|
7
|
+
import { LocalToolExecutor } from './local-tool-executor.js';
|
|
8
|
+
/**
|
|
9
|
+
* Core daemon class — ties config + logger together with lifecycle management.
|
|
10
|
+
* Registers SIGTERM/SIGINT handlers for clean shutdown.
|
|
11
|
+
*/
|
|
12
|
+
export class Daemon {
|
|
13
|
+
config;
|
|
14
|
+
logger;
|
|
15
|
+
healthIntervalMs;
|
|
16
|
+
shuttingDown = false;
|
|
17
|
+
keepaliveTimer;
|
|
18
|
+
healthTimer;
|
|
19
|
+
onSigterm;
|
|
20
|
+
onSigint;
|
|
21
|
+
sessionManager;
|
|
22
|
+
discordBot;
|
|
23
|
+
eventBridge;
|
|
24
|
+
orchestrator;
|
|
25
|
+
cloudRuntime;
|
|
26
|
+
constructor(config, logger, healthIntervalMs = 300_000) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.healthIntervalMs = healthIntervalMs;
|
|
30
|
+
this.onSigterm = () => void this.shutdown();
|
|
31
|
+
this.onSigint = () => void this.shutdown();
|
|
32
|
+
}
|
|
33
|
+
/** Start the daemon: log startup info, register signal handlers, start keepalive. */
|
|
34
|
+
async start() {
|
|
35
|
+
this.sessionManager = new SessionManager(this.logger);
|
|
36
|
+
this.logger.info('daemon started', {
|
|
37
|
+
log_level: this.config.log.level,
|
|
38
|
+
scan_roots: this.config.projects.scan_roots.length,
|
|
39
|
+
discord_configured: !!this.config.discord,
|
|
40
|
+
cloud_configured: !!this.config.cloud?.device_token,
|
|
41
|
+
});
|
|
42
|
+
process.on('SIGTERM', this.onSigterm);
|
|
43
|
+
process.on('SIGINT', this.onSigint);
|
|
44
|
+
// Keep the event loop alive. The write stream alone doesn't hold a ref
|
|
45
|
+
// when there's no pending I/O, so we need an explicit timer.
|
|
46
|
+
this.keepaliveTimer = setInterval(() => { }, 60_000);
|
|
47
|
+
// Conditionally start Discord bot if config is present and valid
|
|
48
|
+
if (this.config.discord?.token) {
|
|
49
|
+
try {
|
|
50
|
+
validateDiscordConfig(this.config.discord);
|
|
51
|
+
this.discordBot = new DiscordBot({
|
|
52
|
+
config: this.config.discord,
|
|
53
|
+
logger: this.logger,
|
|
54
|
+
sessionManager: this.sessionManager,
|
|
55
|
+
scanProjects: () => this.scanProjects(),
|
|
56
|
+
});
|
|
57
|
+
await this.discordBot.login();
|
|
58
|
+
// Wire up EventBridge after bot is ready
|
|
59
|
+
const channelManager = this.discordBot.getChannelManager();
|
|
60
|
+
const client = this.discordBot.getClient();
|
|
61
|
+
if (channelManager && client) {
|
|
62
|
+
this.eventBridge = new EventBridge({
|
|
63
|
+
sessionManager: this.sessionManager,
|
|
64
|
+
channelManager,
|
|
65
|
+
client,
|
|
66
|
+
config: this.config,
|
|
67
|
+
logger: this.logger,
|
|
68
|
+
ownerId: this.config.discord.owner_id,
|
|
69
|
+
});
|
|
70
|
+
this.discordBot.setEventBridge(this.eventBridge);
|
|
71
|
+
this.eventBridge.start();
|
|
72
|
+
this.logger.info('event bridge wired');
|
|
73
|
+
// Wire up Orchestrator if control_channel_id is configured
|
|
74
|
+
if (this.config.discord.control_channel_id) {
|
|
75
|
+
this.orchestrator = new Orchestrator({
|
|
76
|
+
sessionManager: this.sessionManager,
|
|
77
|
+
channelManager,
|
|
78
|
+
scanProjects: () => this.scanProjects(),
|
|
79
|
+
config: {
|
|
80
|
+
model: this.config.discord.orchestrator?.model ?? 'claude-haiku-4-5-20251001',
|
|
81
|
+
max_tokens: this.config.discord.orchestrator?.max_tokens ?? 1024,
|
|
82
|
+
control_channel_id: this.config.discord.control_channel_id,
|
|
83
|
+
},
|
|
84
|
+
logger: this.logger,
|
|
85
|
+
ownerId: this.config.discord.owner_id,
|
|
86
|
+
});
|
|
87
|
+
client.on('messageCreate', (message) => {
|
|
88
|
+
void this.orchestrator.handleMessage(message);
|
|
89
|
+
});
|
|
90
|
+
this.logger.info('orchestrator wired', {
|
|
91
|
+
control_channel_id: this.config.discord.control_channel_id,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.logger.warn('event bridge skipped — channel manager or client not available');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
// Log error but don't abort daemon startup — bot is optional
|
|
101
|
+
this.logger.error('discord bot login failed', {
|
|
102
|
+
error: err instanceof Error ? err.message : String(err),
|
|
103
|
+
});
|
|
104
|
+
this.discordBot = undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (this.config.cloud?.device_token && this.config.cloud?.runtime_id && this.config.cloud.enabled !== false) {
|
|
108
|
+
const executor = new LocalToolExecutor(this.sessionManager, () => this.scanProjects());
|
|
109
|
+
this.cloudRuntime = new CloudRuntime(this.config.cloud, executor, this.logger);
|
|
110
|
+
this.cloudRuntime.start();
|
|
111
|
+
this.logger.info('cloud runtime wired', {
|
|
112
|
+
gateway_url: this.config.cloud.gateway_url,
|
|
113
|
+
runtime_id: this.config.cloud.runtime_id,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// Health heartbeat — logs uptime, session count, Discord status, memory
|
|
117
|
+
const startTime = Date.now();
|
|
118
|
+
this.healthTimer = setInterval(() => {
|
|
119
|
+
const sessions = this.sessionManager?.getAllSessions() ?? [];
|
|
120
|
+
const activeSessions = sessions.filter((s) => s.status === 'running' || s.status === 'blocked').length;
|
|
121
|
+
this.logger.info('health', {
|
|
122
|
+
uptime_s: Math.floor((Date.now() - startTime) / 1000),
|
|
123
|
+
active_sessions: activeSessions,
|
|
124
|
+
discord_connected: !!this.discordBot?.getClient()?.isReady(),
|
|
125
|
+
memory_rss_mb: Math.round(process.memoryUsage().rss / 1024 / 1024),
|
|
126
|
+
});
|
|
127
|
+
}, this.healthIntervalMs);
|
|
128
|
+
}
|
|
129
|
+
/** Scan configured project roots for project directories. */
|
|
130
|
+
async scanProjects() {
|
|
131
|
+
return scanForProjects(this.config.projects.scan_roots);
|
|
132
|
+
}
|
|
133
|
+
/** Accessor for the session manager (available after start()). */
|
|
134
|
+
getSessionManager() {
|
|
135
|
+
if (!this.sessionManager) {
|
|
136
|
+
throw new Error('Daemon not started — call start() before accessing the session manager');
|
|
137
|
+
}
|
|
138
|
+
return this.sessionManager;
|
|
139
|
+
}
|
|
140
|
+
/** Accessor for the event bridge (available after start() with Discord configured). */
|
|
141
|
+
getEventBridge() {
|
|
142
|
+
return this.eventBridge;
|
|
143
|
+
}
|
|
144
|
+
/** Accessor for the orchestrator (available after start() with control_channel_id configured). */
|
|
145
|
+
getOrchestrator() {
|
|
146
|
+
return this.orchestrator;
|
|
147
|
+
}
|
|
148
|
+
/** Idempotent shutdown: log, cleanup sessions, close logger, exit. */
|
|
149
|
+
async shutdown() {
|
|
150
|
+
if (this.shuttingDown)
|
|
151
|
+
return;
|
|
152
|
+
this.shuttingDown = true;
|
|
153
|
+
this.logger.info('daemon shutting down');
|
|
154
|
+
// Remove signal handlers to avoid double-fire
|
|
155
|
+
process.removeListener('SIGTERM', this.onSigterm);
|
|
156
|
+
process.removeListener('SIGINT', this.onSigint);
|
|
157
|
+
// Clear health heartbeat timer
|
|
158
|
+
if (this.healthTimer) {
|
|
159
|
+
clearInterval(this.healthTimer);
|
|
160
|
+
this.healthTimer = undefined;
|
|
161
|
+
}
|
|
162
|
+
// Clear keepalive so the event loop can drain
|
|
163
|
+
if (this.keepaliveTimer) {
|
|
164
|
+
clearInterval(this.keepaliveTimer);
|
|
165
|
+
this.keepaliveTimer = undefined;
|
|
166
|
+
}
|
|
167
|
+
// Stop Orchestrator first
|
|
168
|
+
if (this.orchestrator) {
|
|
169
|
+
this.orchestrator.stop();
|
|
170
|
+
this.orchestrator = undefined;
|
|
171
|
+
}
|
|
172
|
+
if (this.cloudRuntime) {
|
|
173
|
+
this.cloudRuntime.stop();
|
|
174
|
+
this.cloudRuntime = undefined;
|
|
175
|
+
}
|
|
176
|
+
// Stop EventBridge before Discord bot destroy
|
|
177
|
+
if (this.eventBridge) {
|
|
178
|
+
await this.eventBridge.stop();
|
|
179
|
+
this.eventBridge = undefined;
|
|
180
|
+
}
|
|
181
|
+
// Destroy Discord bot before session cleanup
|
|
182
|
+
if (this.discordBot) {
|
|
183
|
+
await this.discordBot.destroy();
|
|
184
|
+
this.discordBot = undefined;
|
|
185
|
+
}
|
|
186
|
+
// Clean up active sessions before closing logger
|
|
187
|
+
if (this.sessionManager) {
|
|
188
|
+
await this.sessionManager.cleanup();
|
|
189
|
+
}
|
|
190
|
+
await this.logger.close();
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DaemonConfig, ProjectInfo } from './types.js';\nimport type { Logger } from './logger.js';\nimport { SessionManager } from './session-manager.js';\nimport { scanForProjects } from './project-scanner.js';\nimport { DiscordBot, validateDiscordConfig } from './discord-bot.js';\nimport { EventBridge } from './event-bridge.js';\nimport { Orchestrator } from './orchestrator.js';\nimport { CloudRuntime } from './cloud-runtime.js';\nimport { LocalToolExecutor } from './local-tool-executor.js';\n\n/**\n * Core daemon class — ties config + logger together with lifecycle management.\n * Registers SIGTERM/SIGINT handlers for clean shutdown.\n */\nexport class Daemon {\n private shuttingDown = false;\n private keepaliveTimer: ReturnType<typeof setInterval> | undefined;\n private healthTimer: ReturnType<typeof setInterval> | undefined;\n private readonly onSigterm: () => void;\n private readonly onSigint: () => void;\n private sessionManager: SessionManager | undefined;\n private discordBot: DiscordBot | undefined;\n private eventBridge: EventBridge | undefined;\n private orchestrator: Orchestrator | undefined;\n private cloudRuntime: CloudRuntime | undefined;\n\n constructor(\n private readonly config: DaemonConfig,\n private readonly logger: Logger,\n private readonly healthIntervalMs: number = 300_000,\n ) {\n this.onSigterm = () => void this.shutdown();\n this.onSigint = () => void this.shutdown();\n }\n\n /** Start the daemon: log startup info, register signal handlers, start keepalive. */\n async start(): Promise<void> {\n this.sessionManager = new SessionManager(this.logger);\n\n this.logger.info('daemon started', {\n log_level: this.config.log.level,\n scan_roots: this.config.projects.scan_roots.length,\n discord_configured: !!this.config.discord,\n cloud_configured: !!this.config.cloud?.device_token,\n });\n\n process.on('SIGTERM', this.onSigterm);\n process.on('SIGINT', this.onSigint);\n\n // Keep the event loop alive. The write stream alone doesn't hold a ref\n // when there's no pending I/O, so we need an explicit timer.\n this.keepaliveTimer = setInterval(() => {}, 60_000);\n\n // Conditionally start Discord bot if config is present and valid\n if (this.config.discord?.token) {\n try {\n validateDiscordConfig(this.config.discord);\n this.discordBot = new DiscordBot({\n config: this.config.discord,\n logger: this.logger,\n sessionManager: this.sessionManager,\n scanProjects: () => this.scanProjects(),\n });\n await this.discordBot.login();\n\n // Wire up EventBridge after bot is ready\n const channelManager = this.discordBot.getChannelManager();\n const client = this.discordBot.getClient();\n if (channelManager && client) {\n this.eventBridge = new EventBridge({\n sessionManager: this.sessionManager,\n channelManager,\n client,\n config: this.config,\n logger: this.logger,\n ownerId: this.config.discord.owner_id,\n });\n this.discordBot.setEventBridge(this.eventBridge);\n this.eventBridge.start();\n this.logger.info('event bridge wired');\n\n // Wire up Orchestrator if control_channel_id is configured\n if (this.config.discord.control_channel_id) {\n this.orchestrator = new Orchestrator({\n sessionManager: this.sessionManager,\n channelManager,\n scanProjects: () => this.scanProjects(),\n config: {\n model: this.config.discord.orchestrator?.model ?? 'claude-haiku-4-5-20251001',\n max_tokens: this.config.discord.orchestrator?.max_tokens ?? 1024,\n control_channel_id: this.config.discord.control_channel_id,\n },\n logger: this.logger,\n ownerId: this.config.discord.owner_id,\n });\n client.on('messageCreate', (message) => {\n void this.orchestrator!.handleMessage(message);\n });\n this.logger.info('orchestrator wired', {\n control_channel_id: this.config.discord.control_channel_id,\n });\n }\n } else {\n this.logger.warn('event bridge skipped — channel manager or client not available');\n }\n } catch (err) {\n // Log error but don't abort daemon startup — bot is optional\n this.logger.error('discord bot login failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n this.discordBot = undefined;\n }\n }\n\n if (this.config.cloud?.device_token && this.config.cloud?.runtime_id && this.config.cloud.enabled !== false) {\n const executor = new LocalToolExecutor(this.sessionManager, () => this.scanProjects());\n this.cloudRuntime = new CloudRuntime(this.config.cloud, executor, this.logger);\n this.cloudRuntime.start();\n this.logger.info('cloud runtime wired', {\n gateway_url: this.config.cloud.gateway_url,\n runtime_id: this.config.cloud.runtime_id,\n });\n }\n\n // Health heartbeat — logs uptime, session count, Discord status, memory\n const startTime = Date.now();\n this.healthTimer = setInterval(() => {\n const sessions = this.sessionManager?.getAllSessions() ?? [];\n const activeSessions = sessions.filter(\n (s) => s.status === 'running' || s.status === 'blocked',\n ).length;\n this.logger.info('health', {\n uptime_s: Math.floor((Date.now() - startTime) / 1000),\n active_sessions: activeSessions,\n discord_connected: !!this.discordBot?.getClient()?.isReady(),\n memory_rss_mb: Math.round(process.memoryUsage().rss / 1024 / 1024),\n });\n }, this.healthIntervalMs);\n }\n\n /** Scan configured project roots for project directories. */\n async scanProjects(): Promise<ProjectInfo[]> {\n return scanForProjects(this.config.projects.scan_roots);\n }\n\n /** Accessor for the session manager (available after start()). */\n getSessionManager(): SessionManager {\n if (!this.sessionManager) {\n throw new Error('Daemon not started — call start() before accessing the session manager');\n }\n return this.sessionManager;\n }\n\n /** Accessor for the event bridge (available after start() with Discord configured). */\n getEventBridge(): EventBridge | undefined {\n return this.eventBridge;\n }\n\n /** Accessor for the orchestrator (available after start() with control_channel_id configured). */\n getOrchestrator(): Orchestrator | undefined {\n return this.orchestrator;\n }\n\n /** Idempotent shutdown: log, cleanup sessions, close logger, exit. */\n async shutdown(): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n\n this.logger.info('daemon shutting down');\n\n // Remove signal handlers to avoid double-fire\n process.removeListener('SIGTERM', this.onSigterm);\n process.removeListener('SIGINT', this.onSigint);\n\n // Clear health heartbeat timer\n if (this.healthTimer) {\n clearInterval(this.healthTimer);\n this.healthTimer = undefined;\n }\n\n // Clear keepalive so the event loop can drain\n if (this.keepaliveTimer) {\n clearInterval(this.keepaliveTimer);\n this.keepaliveTimer = undefined;\n }\n\n // Stop Orchestrator first\n if (this.orchestrator) {\n this.orchestrator.stop();\n this.orchestrator = undefined;\n }\n\n if (this.cloudRuntime) {\n this.cloudRuntime.stop();\n this.cloudRuntime = undefined;\n }\n\n // Stop EventBridge before Discord bot destroy\n if (this.eventBridge) {\n await this.eventBridge.stop();\n this.eventBridge = undefined;\n }\n\n // Destroy Discord bot before session cleanup\n if (this.discordBot) {\n await this.discordBot.destroy();\n this.discordBot = undefined;\n }\n\n // Clean up active sessions before closing logger\n if (this.sessionManager) {\n await this.sessionManager.cleanup();\n }\n\n await this.logger.close();\n process.exit(0);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.test.d.ts","sourceRoot":"","sources":["../src/daemon.test.ts"],"names":[],"mappings":""}
|