@ebowwa/stack 0.1.1 → 0.1.3
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/index.js +112 -28
- package/package.json +1 -1
- package/src/index.ts +143 -35
package/dist/index.js
CHANGED
|
@@ -52387,7 +52387,7 @@ ${taggedContent}`
|
|
|
52387
52387
|
}
|
|
52388
52388
|
function parseMemoryCommand(memory, channel, message) {
|
|
52389
52389
|
const trimmed = message.trim();
|
|
52390
|
-
const isMemoryCommand = trimmed.startsWith("/memory ") || trimmed.startsWith("!memory ");
|
|
52390
|
+
const isMemoryCommand = trimmed === "/memory" || trimmed === "!memory" || trimmed.startsWith("/memory ") || trimmed.startsWith("!memory ");
|
|
52391
52391
|
if (!isMemoryCommand) {
|
|
52392
52392
|
return { handled: false };
|
|
52393
52393
|
}
|
|
@@ -57408,8 +57408,7 @@ class Stack {
|
|
|
57408
57408
|
abortController = null;
|
|
57409
57409
|
constructor(config) {
|
|
57410
57410
|
this.config = {
|
|
57411
|
-
|
|
57412
|
-
telegram: config.telegram ?? {},
|
|
57411
|
+
...config,
|
|
57413
57412
|
api: config.api ?? { port: 8911 },
|
|
57414
57413
|
ralph: config.ralph ?? { worktreesDir: "/root/worktrees", repoUrl: "" },
|
|
57415
57414
|
ai: config.ai ?? { model: "GLM-4.7", temperature: 0.7, maxTokens: 4096 },
|
|
@@ -57418,27 +57417,37 @@ class Stack {
|
|
|
57418
57417
|
this.state = {
|
|
57419
57418
|
started: new Date,
|
|
57420
57419
|
channels: { ssh: false, telegram: false },
|
|
57421
|
-
api: { enabled: !!this.config.api },
|
|
57420
|
+
api: { enabled: !!this.config.api, port: this.config.api?.port },
|
|
57422
57421
|
ralphLoops: new Map
|
|
57423
57422
|
};
|
|
57423
|
+
const memoryChannels = {};
|
|
57424
|
+
const permissions = {};
|
|
57425
|
+
const enabledChannels = [];
|
|
57426
|
+
if (this.config.ssh) {
|
|
57427
|
+
memoryChannels.ssh = { memoryFile: `${this.config.ssh.chatDir}/memory.json`, maxMessages: 50 };
|
|
57428
|
+
enabledChannels.push("ssh");
|
|
57429
|
+
}
|
|
57430
|
+
if (this.config.telegram) {
|
|
57431
|
+
memoryChannels.telegram = { memoryFile: "/root/.telegram-memory.json", maxMessages: 50 };
|
|
57432
|
+
enabledChannels.push("telegram");
|
|
57433
|
+
}
|
|
57434
|
+
if (this.config.api) {
|
|
57435
|
+
memoryChannels.api = { memoryFile: "/root/.api-memory.json", maxMessages: 100 };
|
|
57436
|
+
enabledChannels.push("api");
|
|
57437
|
+
}
|
|
57438
|
+
for (const channel of enabledChannels) {
|
|
57439
|
+
permissions[channel] = { canRead: enabledChannels.filter((c) => c !== channel) };
|
|
57440
|
+
}
|
|
57424
57441
|
this.memory = createPermissionMemory({
|
|
57425
|
-
channels:
|
|
57426
|
-
|
|
57427
|
-
telegram: { memoryFile: "/root/.telegram-memory.json", maxMessages: 50 },
|
|
57428
|
-
api: { memoryFile: "/root/.api-memory.json", maxMessages: 100 }
|
|
57429
|
-
},
|
|
57430
|
-
permissions: {
|
|
57431
|
-
ssh: { canRead: ["telegram", "api"] },
|
|
57432
|
-
telegram: { canRead: ["ssh", "api"] },
|
|
57433
|
-
api: { canRead: ["ssh", "telegram"] }
|
|
57434
|
-
}
|
|
57442
|
+
channels: memoryChannels,
|
|
57443
|
+
permissions
|
|
57435
57444
|
});
|
|
57436
57445
|
this.router = createChannelRouter({
|
|
57437
57446
|
announcement: {
|
|
57438
57447
|
serverName: this.config.node.name,
|
|
57439
57448
|
hostname: this.config.node.hostname,
|
|
57440
57449
|
packageName: "@ebowwa/stack",
|
|
57441
|
-
version: "0.1.
|
|
57450
|
+
version: "0.1.1"
|
|
57442
57451
|
}
|
|
57443
57452
|
});
|
|
57444
57453
|
this.client = new GLMClient;
|
|
@@ -57610,7 +57619,71 @@ Prompt: ${prompt.slice(0, 100)}...`;
|
|
|
57610
57619
|
if (!this.config.api)
|
|
57611
57620
|
return;
|
|
57612
57621
|
const port = this.config.api.port ?? 8911;
|
|
57613
|
-
|
|
57622
|
+
const host = this.config.api.host ?? "0.0.0.0";
|
|
57623
|
+
const server = Bun.serve({
|
|
57624
|
+
port,
|
|
57625
|
+
host,
|
|
57626
|
+
fetch: async (req) => {
|
|
57627
|
+
const url = new URL(req.url);
|
|
57628
|
+
const path = url.pathname;
|
|
57629
|
+
const corsHeaders = {
|
|
57630
|
+
"Access-Control-Allow-Origin": "*",
|
|
57631
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
57632
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
57633
|
+
};
|
|
57634
|
+
if (req.method === "OPTIONS") {
|
|
57635
|
+
return new Response(null, { headers: corsHeaders });
|
|
57636
|
+
}
|
|
57637
|
+
try {
|
|
57638
|
+
if (path === "/api/status" && req.method === "GET") {
|
|
57639
|
+
return Response.json(this.getStatusJSON(), { headers: corsHeaders });
|
|
57640
|
+
}
|
|
57641
|
+
if (path === "/api/ralph-loops" && req.method === "GET") {
|
|
57642
|
+
return Response.json(this.listRalphLoopsJSON(), { headers: corsHeaders });
|
|
57643
|
+
}
|
|
57644
|
+
if (path === "/api/ralph-loops" && req.method === "POST") {
|
|
57645
|
+
const body = await req.json();
|
|
57646
|
+
const prompt = body.prompt;
|
|
57647
|
+
if (!prompt) {
|
|
57648
|
+
return Response.json({ error: "Missing prompt" }, { status: 400, headers: corsHeaders });
|
|
57649
|
+
}
|
|
57650
|
+
const result = await this.startRalphLoop(prompt);
|
|
57651
|
+
return Response.json({ message: result }, { headers: corsHeaders });
|
|
57652
|
+
}
|
|
57653
|
+
const match = path.match(/^\/api\/ralph-loops\/(.+)$/);
|
|
57654
|
+
if (match && req.method === "DELETE") {
|
|
57655
|
+
const result = await this.stopRalphLoop(match[1]);
|
|
57656
|
+
return Response.json({ message: result }, { headers: corsHeaders });
|
|
57657
|
+
}
|
|
57658
|
+
if (path === "/health") {
|
|
57659
|
+
return Response.json({ status: "ok" }, { headers: corsHeaders });
|
|
57660
|
+
}
|
|
57661
|
+
return Response.json({ error: "Not found" }, { status: 404, headers: corsHeaders });
|
|
57662
|
+
} catch (error) {
|
|
57663
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
57664
|
+
return Response.json({ error: errorMsg }, { status: 500, headers: corsHeaders });
|
|
57665
|
+
}
|
|
57666
|
+
}
|
|
57667
|
+
});
|
|
57668
|
+
console.log(`[Stack] API running on http://${host}:${port}`);
|
|
57669
|
+
}
|
|
57670
|
+
getStatusJSON() {
|
|
57671
|
+
return {
|
|
57672
|
+
node: this.config.node.name,
|
|
57673
|
+
channels: {
|
|
57674
|
+
ssh: this.state.channels.ssh,
|
|
57675
|
+
telegram: this.state.channels.telegram
|
|
57676
|
+
},
|
|
57677
|
+
api: {
|
|
57678
|
+
enabled: this.state.api.enabled,
|
|
57679
|
+
port: this.state.api.port
|
|
57680
|
+
},
|
|
57681
|
+
ralphLoops: this.state.ralphLoops.size,
|
|
57682
|
+
uptime: Math.floor((Date.now() - this.state.started.getTime()) / 1000)
|
|
57683
|
+
};
|
|
57684
|
+
}
|
|
57685
|
+
listRalphLoopsJSON() {
|
|
57686
|
+
return Array.from(this.state.ralphLoops.values());
|
|
57614
57687
|
}
|
|
57615
57688
|
async start() {
|
|
57616
57689
|
console.log(`[Stack] Starting ${this.config.node.name}...`);
|
|
@@ -57621,10 +57694,15 @@ Prompt: ${prompt.slice(0, 100)}...`;
|
|
|
57621
57694
|
await this.router.start();
|
|
57622
57695
|
this.startAPI();
|
|
57623
57696
|
console.log("[Stack] Running!");
|
|
57624
|
-
|
|
57697
|
+
if (this.state.channels.ssh) {
|
|
57698
|
+
console.log(` - SSH: echo 'msg' > ${this.config.ssh.chatDir}/in`);
|
|
57699
|
+
}
|
|
57625
57700
|
if (this.state.channels.telegram) {
|
|
57626
57701
|
console.log(" - Telegram: enabled");
|
|
57627
57702
|
}
|
|
57703
|
+
if (this.state.api.enabled) {
|
|
57704
|
+
console.log(` - API: :${this.state.api.port}`);
|
|
57705
|
+
}
|
|
57628
57706
|
console.log(" - Commands: /ralph start|list|stop, /status, /memory <cmd>");
|
|
57629
57707
|
await new Promise(() => {});
|
|
57630
57708
|
}
|
|
@@ -57640,17 +57718,22 @@ Prompt: ${prompt.slice(0, 100)}...`;
|
|
|
57640
57718
|
}
|
|
57641
57719
|
}
|
|
57642
57720
|
async function main() {
|
|
57643
|
-
const
|
|
57644
|
-
|
|
57645
|
-
|
|
57646
|
-
|
|
57647
|
-
|
|
57648
|
-
|
|
57649
|
-
|
|
57650
|
-
|
|
57651
|
-
|
|
57721
|
+
const config = {
|
|
57722
|
+
...process.env.SSH_CHAT_DIR ? {
|
|
57723
|
+
ssh: {
|
|
57724
|
+
chatDir: process.env.SSH_CHAT_DIR,
|
|
57725
|
+
pollInterval: parseInt(process.env.SSH_POLL_INTERVAL || "500", 10)
|
|
57726
|
+
}
|
|
57727
|
+
} : {},
|
|
57728
|
+
...process.env.TELEGRAM_BOT_TOKEN ? {
|
|
57729
|
+
telegram: {
|
|
57730
|
+
botToken: process.env.TELEGRAM_BOT_TOKEN,
|
|
57731
|
+
allowedChats: process.env.TELEGRAM_CHAT_ID ? [parseInt(process.env.TELEGRAM_CHAT_ID, 10)] : undefined
|
|
57732
|
+
}
|
|
57733
|
+
} : {},
|
|
57652
57734
|
api: {
|
|
57653
|
-
port: parseInt(process.env.API_PORT || "8911", 10)
|
|
57735
|
+
port: parseInt(process.env.API_PORT || "8911", 10),
|
|
57736
|
+
host: process.env.API_HOST
|
|
57654
57737
|
},
|
|
57655
57738
|
ralph: {
|
|
57656
57739
|
worktreesDir: process.env.WORKTREES_DIR || "/root/worktrees",
|
|
@@ -57660,7 +57743,8 @@ async function main() {
|
|
|
57660
57743
|
name: process.env.NODE_NAME || "stack",
|
|
57661
57744
|
hostname: process.env.HOSTNAME || "localhost"
|
|
57662
57745
|
}
|
|
57663
|
-
}
|
|
57746
|
+
};
|
|
57747
|
+
const stack = new Stack(config);
|
|
57664
57748
|
process.on("SIGINT", async () => {
|
|
57665
57749
|
await stack.stop();
|
|
57666
57750
|
process.exit(0);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -94,7 +94,7 @@ interface RalphLoopInfo {
|
|
|
94
94
|
// ============================================================
|
|
95
95
|
|
|
96
96
|
export class Stack {
|
|
97
|
-
private config:
|
|
97
|
+
private config: StackConfig;
|
|
98
98
|
private state: StackState;
|
|
99
99
|
private router: ReturnType<typeof createChannelRouter>;
|
|
100
100
|
private memory: ReturnType<typeof createPermissionMemory>;
|
|
@@ -104,34 +104,50 @@ export class Stack {
|
|
|
104
104
|
private abortController: AbortController | null = null;
|
|
105
105
|
|
|
106
106
|
constructor(config: StackConfig) {
|
|
107
|
+
// Only set defaults for non-channel config
|
|
107
108
|
this.config = {
|
|
108
|
-
|
|
109
|
-
telegram: config.telegram ?? {},
|
|
109
|
+
...config,
|
|
110
110
|
api: config.api ?? { port: 8911 },
|
|
111
111
|
ralph: config.ralph ?? { worktreesDir: "/root/worktrees", repoUrl: "" },
|
|
112
112
|
ai: config.ai ?? { model: "GLM-4.7", temperature: 0.7, maxTokens: 4096 },
|
|
113
113
|
node: config.node ?? { name: "stack", hostname: "localhost" },
|
|
114
|
+
// ssh and telegram remain undefined if not provided
|
|
114
115
|
};
|
|
115
116
|
|
|
116
117
|
this.state = {
|
|
117
118
|
started: new Date(),
|
|
118
119
|
channels: { ssh: false, telegram: false },
|
|
119
|
-
api: { enabled: !!this.config.api },
|
|
120
|
+
api: { enabled: !!this.config.api, port: this.config.api?.port },
|
|
120
121
|
ralphLoops: new Map(),
|
|
121
122
|
};
|
|
122
123
|
|
|
123
|
-
//
|
|
124
|
+
// Build memory channels dynamically based on enabled channels
|
|
125
|
+
const memoryChannels: Record<string, { memoryFile: string; maxMessages: number }> = {};
|
|
126
|
+
const permissions: Record<string, { canRead: string[] }> = {};
|
|
127
|
+
const enabledChannels: string[] = [];
|
|
128
|
+
|
|
129
|
+
if (this.config.ssh) {
|
|
130
|
+
memoryChannels.ssh = { memoryFile: `${this.config.ssh.chatDir}/memory.json`, maxMessages: 50 };
|
|
131
|
+
enabledChannels.push("ssh");
|
|
132
|
+
}
|
|
133
|
+
if (this.config.telegram) {
|
|
134
|
+
memoryChannels.telegram = { memoryFile: "/root/.telegram-memory.json", maxMessages: 50 };
|
|
135
|
+
enabledChannels.push("telegram");
|
|
136
|
+
}
|
|
137
|
+
if (this.config.api) {
|
|
138
|
+
memoryChannels.api = { memoryFile: "/root/.api-memory.json", maxMessages: 100 };
|
|
139
|
+
enabledChannels.push("api");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Set up cross-channel permissions for enabled channels
|
|
143
|
+
for (const channel of enabledChannels) {
|
|
144
|
+
permissions[channel] = { canRead: enabledChannels.filter(c => c !== channel) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Initialize shared memory (empty if no channels)
|
|
124
148
|
this.memory = createPermissionMemory({
|
|
125
|
-
channels:
|
|
126
|
-
|
|
127
|
-
telegram: { memoryFile: "/root/.telegram-memory.json", maxMessages: 50 },
|
|
128
|
-
api: { memoryFile: "/root/.api-memory.json", maxMessages: 100 },
|
|
129
|
-
},
|
|
130
|
-
permissions: {
|
|
131
|
-
ssh: { canRead: ["telegram", "api"] },
|
|
132
|
-
telegram: { canRead: ["ssh", "api"] },
|
|
133
|
-
api: { canRead: ["ssh", "telegram"] },
|
|
134
|
-
},
|
|
149
|
+
channels: memoryChannels,
|
|
150
|
+
permissions,
|
|
135
151
|
});
|
|
136
152
|
|
|
137
153
|
// Initialize router
|
|
@@ -140,7 +156,7 @@ export class Stack {
|
|
|
140
156
|
serverName: this.config.node.name,
|
|
141
157
|
hostname: this.config.node.hostname,
|
|
142
158
|
packageName: "@ebowwa/stack",
|
|
143
|
-
version: "0.1.
|
|
159
|
+
version: "0.1.1",
|
|
144
160
|
},
|
|
145
161
|
});
|
|
146
162
|
|
|
@@ -351,20 +367,96 @@ export class Stack {
|
|
|
351
367
|
}
|
|
352
368
|
|
|
353
369
|
// ============================================================
|
|
354
|
-
// HTTP API
|
|
370
|
+
// HTTP API
|
|
355
371
|
// ============================================================
|
|
356
372
|
|
|
357
373
|
private startAPI(): void {
|
|
358
374
|
if (!this.config.api) return;
|
|
359
375
|
|
|
360
376
|
const port = this.config.api.port ?? 8911;
|
|
361
|
-
|
|
377
|
+
const host = this.config.api.host ?? "0.0.0.0";
|
|
378
|
+
|
|
379
|
+
const server = Bun.serve({
|
|
380
|
+
port,
|
|
381
|
+
host,
|
|
382
|
+
fetch: async (req) => {
|
|
383
|
+
const url = new URL(req.url);
|
|
384
|
+
const path = url.pathname;
|
|
385
|
+
|
|
386
|
+
// CORS headers
|
|
387
|
+
const corsHeaders = {
|
|
388
|
+
"Access-Control-Allow-Origin": "*",
|
|
389
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
390
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
if (req.method === "OPTIONS") {
|
|
394
|
+
return new Response(null, { headers: corsHeaders });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
try {
|
|
398
|
+
// GET /api/status
|
|
399
|
+
if (path === "/api/status" && req.method === "GET") {
|
|
400
|
+
return Response.json(this.getStatusJSON(), { headers: corsHeaders });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// GET /api/ralph-loops
|
|
404
|
+
if (path === "/api/ralph-loops" && req.method === "GET") {
|
|
405
|
+
return Response.json(this.listRalphLoopsJSON(), { headers: corsHeaders });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// POST /api/ralph-loops
|
|
409
|
+
if (path === "/api/ralph-loops" && req.method === "POST") {
|
|
410
|
+
const body = await req.json();
|
|
411
|
+
const prompt = body.prompt;
|
|
412
|
+
if (!prompt) {
|
|
413
|
+
return Response.json({ error: "Missing prompt" }, { status: 400, headers: corsHeaders });
|
|
414
|
+
}
|
|
415
|
+
const result = await this.startRalphLoop(prompt);
|
|
416
|
+
return Response.json({ message: result }, { headers: corsHeaders });
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// DELETE /api/ralph-loops/:id
|
|
420
|
+
const match = path.match(/^\/api\/ralph-loops\/(.+)$/);
|
|
421
|
+
if (match && req.method === "DELETE") {
|
|
422
|
+
const result = await this.stopRalphLoop(match[1]);
|
|
423
|
+
return Response.json({ message: result }, { headers: corsHeaders });
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// GET /health
|
|
427
|
+
if (path === "/health") {
|
|
428
|
+
return Response.json({ status: "ok" }, { headers: corsHeaders });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return Response.json({ error: "Not found" }, { status: 404, headers: corsHeaders });
|
|
432
|
+
} catch (error) {
|
|
433
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
434
|
+
return Response.json({ error: errorMsg }, { status: 500, headers: corsHeaders });
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
console.log(`[Stack] API running on http://${host}:${port}`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private getStatusJSON(): object {
|
|
443
|
+
return {
|
|
444
|
+
node: this.config.node.name,
|
|
445
|
+
channels: {
|
|
446
|
+
ssh: this.state.channels.ssh,
|
|
447
|
+
telegram: this.state.channels.telegram,
|
|
448
|
+
},
|
|
449
|
+
api: {
|
|
450
|
+
enabled: this.state.api.enabled,
|
|
451
|
+
port: this.state.api.port,
|
|
452
|
+
},
|
|
453
|
+
ralphLoops: this.state.ralphLoops.size,
|
|
454
|
+
uptime: Math.floor((Date.now() - this.state.started.getTime()) / 1000),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
362
457
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
// GET /api/ralph-loops - List Ralph loops
|
|
366
|
-
// POST /api/ralph-loops - Start Ralph loop
|
|
367
|
-
// DELETE /api/ralph-loops/:id - Stop Ralph loop
|
|
458
|
+
private listRalphLoopsJSON(): object[] {
|
|
459
|
+
return Array.from(this.state.ralphLoops.values());
|
|
368
460
|
}
|
|
369
461
|
|
|
370
462
|
// ============================================================
|
|
@@ -376,7 +468,7 @@ export class Stack {
|
|
|
376
468
|
|
|
377
469
|
this.abortController = new AbortController();
|
|
378
470
|
|
|
379
|
-
// Register channels
|
|
471
|
+
// Register channels (only if configured)
|
|
380
472
|
await this.registerSSH();
|
|
381
473
|
await this.registerTelegram();
|
|
382
474
|
|
|
@@ -390,10 +482,15 @@ export class Stack {
|
|
|
390
482
|
this.startAPI();
|
|
391
483
|
|
|
392
484
|
console.log("[Stack] Running!");
|
|
393
|
-
|
|
485
|
+
if (this.state.channels.ssh) {
|
|
486
|
+
console.log(` - SSH: echo 'msg' > ${this.config.ssh!.chatDir}/in`);
|
|
487
|
+
}
|
|
394
488
|
if (this.state.channels.telegram) {
|
|
395
489
|
console.log(" - Telegram: enabled");
|
|
396
490
|
}
|
|
491
|
+
if (this.state.api.enabled) {
|
|
492
|
+
console.log(` - API: :${this.state.api.port}`);
|
|
493
|
+
}
|
|
397
494
|
console.log(" - Commands: /ralph start|list|stop, /status, /memory <cmd>");
|
|
398
495
|
|
|
399
496
|
// Keep running
|
|
@@ -420,17 +517,26 @@ export class Stack {
|
|
|
420
517
|
// ============================================================
|
|
421
518
|
|
|
422
519
|
async function main() {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
},
|
|
520
|
+
// Build config - only enable channels when explicitly configured
|
|
521
|
+
const config: StackConfig = {
|
|
522
|
+
// SSH only enabled if SSH_CHAT_DIR is set
|
|
523
|
+
...(process.env.SSH_CHAT_DIR ? {
|
|
524
|
+
ssh: {
|
|
525
|
+
chatDir: process.env.SSH_CHAT_DIR,
|
|
526
|
+
pollInterval: parseInt(process.env.SSH_POLL_INTERVAL || "500", 10),
|
|
527
|
+
}
|
|
528
|
+
} : {}),
|
|
529
|
+
// Telegram only enabled if bot token is set
|
|
530
|
+
...(process.env.TELEGRAM_BOT_TOKEN ? {
|
|
531
|
+
telegram: {
|
|
532
|
+
botToken: process.env.TELEGRAM_BOT_TOKEN,
|
|
533
|
+
allowedChats: process.env.TELEGRAM_CHAT_ID ? [parseInt(process.env.TELEGRAM_CHAT_ID, 10)] : undefined,
|
|
534
|
+
}
|
|
535
|
+
} : {}),
|
|
536
|
+
// API enabled by default
|
|
432
537
|
api: {
|
|
433
538
|
port: parseInt(process.env.API_PORT || "8911", 10),
|
|
539
|
+
host: process.env.API_HOST,
|
|
434
540
|
},
|
|
435
541
|
ralph: {
|
|
436
542
|
worktreesDir: process.env.WORKTREES_DIR || "/root/worktrees",
|
|
@@ -440,7 +546,9 @@ async function main() {
|
|
|
440
546
|
name: process.env.NODE_NAME || "stack",
|
|
441
547
|
hostname: process.env.HOSTNAME || "localhost",
|
|
442
548
|
},
|
|
443
|
-
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const stack = new Stack(config);
|
|
444
552
|
|
|
445
553
|
// Handle shutdown
|
|
446
554
|
process.on("SIGINT", async () => {
|