ralph-cli-sandboxed 0.2.9 → 0.4.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/README.md +99 -15
- package/dist/commands/action.d.ts +7 -0
- package/dist/commands/action.js +276 -0
- package/dist/commands/chat.d.ts +8 -0
- package/dist/commands/chat.js +701 -0
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +51 -0
- package/dist/commands/daemon.d.ts +23 -0
- package/dist/commands/daemon.js +422 -0
- package/dist/commands/docker.js +82 -4
- package/dist/commands/fix-config.d.ts +4 -0
- package/dist/commands/fix-config.js +388 -0
- package/dist/commands/help.js +80 -0
- package/dist/commands/init.js +135 -1
- package/dist/commands/listen.d.ts +8 -0
- package/dist/commands/listen.js +280 -0
- package/dist/commands/notify.d.ts +7 -0
- package/dist/commands/notify.js +165 -0
- package/dist/commands/once.js +8 -8
- package/dist/commands/prd.js +2 -2
- package/dist/commands/run.js +25 -12
- package/dist/config/languages.json +4 -0
- package/dist/index.js +14 -0
- package/dist/providers/telegram.d.ts +39 -0
- package/dist/providers/telegram.js +256 -0
- package/dist/templates/macos-scripts.d.ts +42 -0
- package/dist/templates/macos-scripts.js +448 -0
- package/dist/tui/ConfigEditor.d.ts +7 -0
- package/dist/tui/ConfigEditor.js +313 -0
- package/dist/tui/components/ArrayEditor.d.ts +22 -0
- package/dist/tui/components/ArrayEditor.js +193 -0
- package/dist/tui/components/BooleanToggle.d.ts +19 -0
- package/dist/tui/components/BooleanToggle.js +43 -0
- package/dist/tui/components/EditorPanel.d.ts +50 -0
- package/dist/tui/components/EditorPanel.js +232 -0
- package/dist/tui/components/HelpPanel.d.ts +13 -0
- package/dist/tui/components/HelpPanel.js +69 -0
- package/dist/tui/components/JsonSnippetEditor.d.ts +24 -0
- package/dist/tui/components/JsonSnippetEditor.js +380 -0
- package/dist/tui/components/KeyValueEditor.d.ts +34 -0
- package/dist/tui/components/KeyValueEditor.js +261 -0
- package/dist/tui/components/ObjectEditor.d.ts +23 -0
- package/dist/tui/components/ObjectEditor.js +227 -0
- package/dist/tui/components/PresetSelector.d.ts +23 -0
- package/dist/tui/components/PresetSelector.js +58 -0
- package/dist/tui/components/Preview.d.ts +18 -0
- package/dist/tui/components/Preview.js +190 -0
- package/dist/tui/components/ScrollableContainer.d.ts +38 -0
- package/dist/tui/components/ScrollableContainer.js +77 -0
- package/dist/tui/components/SectionNav.d.ts +31 -0
- package/dist/tui/components/SectionNav.js +130 -0
- package/dist/tui/components/StringEditor.d.ts +21 -0
- package/dist/tui/components/StringEditor.js +29 -0
- package/dist/tui/hooks/useConfig.d.ts +16 -0
- package/dist/tui/hooks/useConfig.js +89 -0
- package/dist/tui/hooks/useTerminalSize.d.ts +21 -0
- package/dist/tui/hooks/useTerminalSize.js +48 -0
- package/dist/tui/utils/presets.d.ts +52 -0
- package/dist/tui/utils/presets.js +191 -0
- package/dist/tui/utils/validation.d.ts +49 -0
- package/dist/tui/utils/validation.js +198 -0
- package/dist/utils/chat-client.d.ts +144 -0
- package/dist/utils/chat-client.js +102 -0
- package/dist/utils/config.d.ts +52 -0
- package/dist/utils/daemon-client.d.ts +36 -0
- package/dist/utils/daemon-client.js +70 -0
- package/dist/utils/message-queue.d.ts +58 -0
- package/dist/utils/message-queue.js +133 -0
- package/dist/utils/notification.d.ts +28 -1
- package/dist/utils/notification.js +146 -20
- package/docs/MACOS-DEVELOPMENT.md +435 -0
- package/docs/RALPH-SETUP-TEMPLATE.md +262 -0
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -10,6 +10,13 @@ import { prd, prdAdd, prdList, prdStatus, prdToggle, prdClean, parseListArgs } f
|
|
|
10
10
|
import { docker } from "./commands/docker.js";
|
|
11
11
|
import { prompt } from "./commands/prompt.js";
|
|
12
12
|
import { fixPrd } from "./commands/fix-prd.js";
|
|
13
|
+
import { fixConfig } from "./commands/fix-config.js";
|
|
14
|
+
import { daemon } from "./commands/daemon.js";
|
|
15
|
+
import { notify } from "./commands/notify.js";
|
|
16
|
+
import { chat } from "./commands/chat.js";
|
|
17
|
+
import { listen } from "./commands/listen.js";
|
|
18
|
+
import { config } from "./commands/config.js";
|
|
19
|
+
import { action } from "./commands/action.js";
|
|
13
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
21
|
const __dirname = dirname(__filename);
|
|
15
22
|
function getPackageInfo() {
|
|
@@ -25,7 +32,14 @@ const commands = {
|
|
|
25
32
|
prd,
|
|
26
33
|
prompt,
|
|
27
34
|
docker,
|
|
35
|
+
daemon,
|
|
36
|
+
notify,
|
|
37
|
+
chat,
|
|
38
|
+
listen,
|
|
39
|
+
config,
|
|
40
|
+
action,
|
|
28
41
|
"fix-prd": (args) => fixPrd(args),
|
|
42
|
+
"fix-config": (args) => fixConfig(args),
|
|
29
43
|
// Top-level PRD commands (shortcuts)
|
|
30
44
|
add: () => prdAdd(),
|
|
31
45
|
list: (args) => {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram chat client implementation.
|
|
3
|
+
* Uses the Telegram Bot API with long polling to receive messages.
|
|
4
|
+
*/
|
|
5
|
+
import { ChatClient, ChatCommandHandler, ChatMessageHandler, TelegramSettings, SendMessageOptions } from "../utils/chat-client.js";
|
|
6
|
+
export declare class TelegramChatClient implements ChatClient {
|
|
7
|
+
readonly provider: "telegram";
|
|
8
|
+
private settings;
|
|
9
|
+
private connected;
|
|
10
|
+
private polling;
|
|
11
|
+
private lastUpdateId;
|
|
12
|
+
private pollingTimeout;
|
|
13
|
+
private debug;
|
|
14
|
+
constructor(settings: TelegramSettings, debug?: boolean);
|
|
15
|
+
/**
|
|
16
|
+
* Make a request to the Telegram Bot API.
|
|
17
|
+
*/
|
|
18
|
+
private apiRequest;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a chat ID is allowed.
|
|
21
|
+
*/
|
|
22
|
+
private isChatAllowed;
|
|
23
|
+
/**
|
|
24
|
+
* Start long polling for updates.
|
|
25
|
+
*/
|
|
26
|
+
private poll;
|
|
27
|
+
connect(onCommand: ChatCommandHandler, onMessage?: ChatMessageHandler): Promise<void>;
|
|
28
|
+
sendMessage(chatId: string, text: string, options?: SendMessageOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Answer a callback query (acknowledge button press).
|
|
31
|
+
*/
|
|
32
|
+
answerCallbackQuery(callbackQueryId: string, text?: string): Promise<void>;
|
|
33
|
+
disconnect(): Promise<void>;
|
|
34
|
+
isConnected(): boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a Telegram chat client from settings.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createTelegramClient(settings: TelegramSettings, debug?: boolean): ChatClient;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram chat client implementation.
|
|
3
|
+
* Uses the Telegram Bot API with long polling to receive messages.
|
|
4
|
+
*/
|
|
5
|
+
import https from "https";
|
|
6
|
+
import { parseCommand, } from "../utils/chat-client.js";
|
|
7
|
+
export class TelegramChatClient {
|
|
8
|
+
provider = "telegram";
|
|
9
|
+
settings;
|
|
10
|
+
connected = false;
|
|
11
|
+
polling = false;
|
|
12
|
+
lastUpdateId = 0;
|
|
13
|
+
pollingTimeout = null;
|
|
14
|
+
debug;
|
|
15
|
+
constructor(settings, debug = false) {
|
|
16
|
+
this.settings = settings;
|
|
17
|
+
this.debug = debug;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Make a request to the Telegram Bot API.
|
|
21
|
+
*/
|
|
22
|
+
async apiRequest(method, body) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const url = `https://api.telegram.org/bot${this.settings.botToken}/${method}`;
|
|
25
|
+
const options = {
|
|
26
|
+
method: body ? "POST" : "GET",
|
|
27
|
+
headers: body
|
|
28
|
+
? {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
}
|
|
31
|
+
: undefined,
|
|
32
|
+
};
|
|
33
|
+
const req = https.request(url, options, (res) => {
|
|
34
|
+
let data = "";
|
|
35
|
+
res.on("data", (chunk) => {
|
|
36
|
+
data += chunk;
|
|
37
|
+
});
|
|
38
|
+
res.on("end", () => {
|
|
39
|
+
try {
|
|
40
|
+
const response = JSON.parse(data);
|
|
41
|
+
if (response.ok && response.result !== undefined) {
|
|
42
|
+
resolve(response.result);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
reject(new Error(`Telegram API error: ${response.description || "Unknown error"} (code: ${response.error_code})`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
reject(new Error(`Failed to parse Telegram response: ${data}`));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
req.on("error", (err) => {
|
|
54
|
+
reject(new Error(`Telegram request failed: ${err.message}`));
|
|
55
|
+
});
|
|
56
|
+
if (body) {
|
|
57
|
+
req.write(JSON.stringify(body));
|
|
58
|
+
}
|
|
59
|
+
req.end();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if a chat ID is allowed.
|
|
64
|
+
*/
|
|
65
|
+
isChatAllowed(chatId) {
|
|
66
|
+
// If no allowed chat IDs specified, allow all
|
|
67
|
+
if (!this.settings.allowedChatIds || this.settings.allowedChatIds.length === 0) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return this.settings.allowedChatIds.includes(chatId);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Start long polling for updates.
|
|
74
|
+
*/
|
|
75
|
+
async poll(onCommand, onMessage) {
|
|
76
|
+
if (!this.polling)
|
|
77
|
+
return;
|
|
78
|
+
try {
|
|
79
|
+
const updates = await this.apiRequest("getUpdates", {
|
|
80
|
+
offset: this.lastUpdateId + 1,
|
|
81
|
+
timeout: 30, // Long polling timeout in seconds
|
|
82
|
+
});
|
|
83
|
+
for (const update of updates) {
|
|
84
|
+
this.lastUpdateId = update.update_id;
|
|
85
|
+
// Handle callback queries (button presses)
|
|
86
|
+
if (update.callback_query) {
|
|
87
|
+
const callbackQuery = update.callback_query;
|
|
88
|
+
const chatId = callbackQuery.message?.chat?.id
|
|
89
|
+
? String(callbackQuery.message.chat.id)
|
|
90
|
+
: null;
|
|
91
|
+
if (chatId && this.isChatAllowed(chatId) && callbackQuery.data) {
|
|
92
|
+
// Acknowledge the callback query
|
|
93
|
+
try {
|
|
94
|
+
await this.answerCallbackQuery(callbackQuery.id);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (this.debug) {
|
|
98
|
+
console.error(`[telegram] Failed to answer callback query: ${err}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Create a synthetic message from the callback data
|
|
102
|
+
// The callback_data is the command string (e.g., "/run feature")
|
|
103
|
+
const message = {
|
|
104
|
+
text: callbackQuery.data,
|
|
105
|
+
chatId,
|
|
106
|
+
senderId: String(callbackQuery.from.id),
|
|
107
|
+
senderName: [callbackQuery.from.first_name, callbackQuery.from.last_name]
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join(" "),
|
|
110
|
+
timestamp: new Date(),
|
|
111
|
+
raw: update,
|
|
112
|
+
};
|
|
113
|
+
// Parse and execute the command
|
|
114
|
+
const command = parseCommand(message.text, message);
|
|
115
|
+
if (command) {
|
|
116
|
+
try {
|
|
117
|
+
await onCommand(command);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (this.debug) {
|
|
121
|
+
console.error(`[telegram] Callback command error: ${err}`);
|
|
122
|
+
}
|
|
123
|
+
await this.sendMessage(chatId, `Error executing command: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (update.message?.text) {
|
|
130
|
+
const chatId = String(update.message.chat.id);
|
|
131
|
+
// Check if chat is allowed
|
|
132
|
+
if (!this.isChatAllowed(chatId)) {
|
|
133
|
+
if (this.debug) {
|
|
134
|
+
console.log(`[telegram] Ignoring message from unauthorized chat: ${chatId}`);
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const message = {
|
|
139
|
+
text: update.message.text,
|
|
140
|
+
chatId,
|
|
141
|
+
senderId: update.message.from ? String(update.message.from.id) : undefined,
|
|
142
|
+
senderName: update.message.from
|
|
143
|
+
? [update.message.from.first_name, update.message.from.last_name].filter(Boolean).join(" ")
|
|
144
|
+
: undefined,
|
|
145
|
+
timestamp: new Date(update.message.date * 1000),
|
|
146
|
+
raw: update,
|
|
147
|
+
};
|
|
148
|
+
// Call raw message handler if provided
|
|
149
|
+
if (onMessage) {
|
|
150
|
+
try {
|
|
151
|
+
await onMessage(message);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
if (this.debug) {
|
|
155
|
+
console.error(`[telegram] Message handler error: ${err}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Try to parse as a command
|
|
160
|
+
const command = parseCommand(message.text, message);
|
|
161
|
+
if (command) {
|
|
162
|
+
try {
|
|
163
|
+
await onCommand(command);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (this.debug) {
|
|
167
|
+
console.error(`[telegram] Command handler error: ${err}`);
|
|
168
|
+
}
|
|
169
|
+
// Send error message to chat
|
|
170
|
+
await this.sendMessage(chatId, `Error executing command: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
if (this.debug) {
|
|
178
|
+
console.error(`[telegram] Poll error: ${err}`);
|
|
179
|
+
}
|
|
180
|
+
// Wait a bit before retrying on error
|
|
181
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
182
|
+
}
|
|
183
|
+
// Schedule next poll
|
|
184
|
+
if (this.polling) {
|
|
185
|
+
this.pollingTimeout = setTimeout(() => this.poll(onCommand, onMessage), 100);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async connect(onCommand, onMessage) {
|
|
189
|
+
if (this.connected) {
|
|
190
|
+
throw new Error("Already connected");
|
|
191
|
+
}
|
|
192
|
+
// Verify bot token by calling getMe
|
|
193
|
+
try {
|
|
194
|
+
const me = await this.apiRequest("getMe");
|
|
195
|
+
if (this.debug) {
|
|
196
|
+
console.log(`[telegram] Connected as @${me.username || me.first_name} (ID: ${me.id})`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
throw new Error(`Failed to connect to Telegram: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
201
|
+
}
|
|
202
|
+
this.connected = true;
|
|
203
|
+
this.polling = true;
|
|
204
|
+
// Start polling
|
|
205
|
+
this.poll(onCommand, onMessage);
|
|
206
|
+
}
|
|
207
|
+
async sendMessage(chatId, text, options) {
|
|
208
|
+
if (!this.connected) {
|
|
209
|
+
throw new Error("Not connected");
|
|
210
|
+
}
|
|
211
|
+
const body = {
|
|
212
|
+
chat_id: chatId,
|
|
213
|
+
text,
|
|
214
|
+
parse_mode: "HTML",
|
|
215
|
+
};
|
|
216
|
+
// Convert generic InlineButton format to Telegram's InlineKeyboardMarkup
|
|
217
|
+
if (options?.inlineKeyboard && options.inlineKeyboard.length > 0) {
|
|
218
|
+
const inlineKeyboard = options.inlineKeyboard.map((row) => row.map((button) => ({
|
|
219
|
+
text: button.text,
|
|
220
|
+
callback_data: button.callbackData,
|
|
221
|
+
url: button.url,
|
|
222
|
+
})));
|
|
223
|
+
body.reply_markup = { inline_keyboard: inlineKeyboard };
|
|
224
|
+
}
|
|
225
|
+
await this.apiRequest("sendMessage", body);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Answer a callback query (acknowledge button press).
|
|
229
|
+
*/
|
|
230
|
+
async answerCallbackQuery(callbackQueryId, text) {
|
|
231
|
+
if (!this.connected) {
|
|
232
|
+
throw new Error("Not connected");
|
|
233
|
+
}
|
|
234
|
+
await this.apiRequest("answerCallbackQuery", {
|
|
235
|
+
callback_query_id: callbackQueryId,
|
|
236
|
+
text,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
async disconnect() {
|
|
240
|
+
this.polling = false;
|
|
241
|
+
this.connected = false;
|
|
242
|
+
if (this.pollingTimeout) {
|
|
243
|
+
clearTimeout(this.pollingTimeout);
|
|
244
|
+
this.pollingTimeout = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
isConnected() {
|
|
248
|
+
return this.connected;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Create a Telegram chat client from settings.
|
|
253
|
+
*/
|
|
254
|
+
export function createTelegramClient(settings, debug = false) {
|
|
255
|
+
return new TelegramChatClient(settings, debug);
|
|
256
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS/Swift development script templates
|
|
3
|
+
* These scripts are generated when Swift + SwiftUI is selected during ralph init
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate gen_xcode.sh script content
|
|
7
|
+
* This script generates an Xcode project from a Swift package, supporting SwiftUI apps
|
|
8
|
+
*
|
|
9
|
+
* Usage: ./scripts/gen_xcode.sh [project_name]
|
|
10
|
+
*
|
|
11
|
+
* @param projectName - Default project name (can be overridden by script argument)
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateGenXcodeScript(projectName?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Check if the selected technologies include SwiftUI
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasSwiftUI(technologies: string[]): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check if the selected technologies include Fastlane
|
|
20
|
+
*/
|
|
21
|
+
export declare function hasFastlane(technologies: string[]): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Generate Fastfile template for macOS/iOS deployment
|
|
24
|
+
* This creates a standard Fastfile with beta and release lanes
|
|
25
|
+
*
|
|
26
|
+
* @param projectName - Name of the Xcode project/app
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateFastfile(projectName?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate Appfile template for Fastlane
|
|
31
|
+
* This contains app-specific configuration like bundle ID and Apple ID
|
|
32
|
+
*
|
|
33
|
+
* @param projectName - Name of the Xcode project/app
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateAppfile(projectName?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Generate README section for Fastlane setup
|
|
38
|
+
* This provides documentation on how to use Fastlane with the project
|
|
39
|
+
*
|
|
40
|
+
* @param projectName - Name of the Xcode project/app
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateFastlaneReadmeSection(projectName?: string): string;
|