Skip to content

Commit 7de6fc8

Browse files
authored
Merge pull request #22 from G-Core/fix/codespace-autorun
create triggerFile autorun commands
2 parents 5a7fa50 + 69cf8a3 commit 7de6fc8

4 files changed

Lines changed: 208 additions & 2 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "fastedge",
33
"displayName": "FastEdge Launcher",
4-
"version": "0.1.8",
4+
"version": "0.1.12",
55
"publisher": "g-corelabssa",
66
"description": "Launcher for FastEdge apps",
77
"icon": "images/fastedge.png",
@@ -15,7 +15,7 @@
1515
"Debuggers"
1616
],
1717
"activationEvents": [
18-
"onDebug"
18+
"onStartupFinished"
1919
],
2020
"main": "./dist/extension.js",
2121
"scripts": {

src/autorun/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { initializeTriggerFileHandler } from "./triggerFileHandler";

src/autorun/triggerFileHandler.ts

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import * as vscode from "vscode";
2+
3+
/*
4+
USAGE EXAMPLE FOR TRIGGER FILE (.vscode/.fastedge-run-command):
5+
----------------------------------------
6+
Simple command (no args):
7+
fastedge.generate-launch-json
8+
9+
OR JSON format with args:
10+
{
11+
"command": "fastedge.generate-mcp-json",
12+
"args": ["optionalArg1", 42]
13+
}
14+
----------------------------------------
15+
e.g. to auto-generate launch.json on startup.
16+
echo "fastedge.generate-launch-json" > .vscode/.fastedge-run-command
17+
*/
18+
19+
/**
20+
* Trigger file configuration
21+
*/
22+
const TRIGGER_FILE_PATH = ".vscode/.fastedge-run-command";
23+
24+
/**
25+
* Allowlist of commands that can be executed via trigger file
26+
* This is a security measure to prevent arbitrary command execution
27+
*/
28+
const ALLOWED_COMMANDS = [
29+
"fastedge.setup-codespace-secret",
30+
"fastedge.generate-launch-json",
31+
"fastedge.generate-mcp-json",
32+
"fastedge.run-file",
33+
"fastedge.run-workspace",
34+
"workbench.action.reloadWindow",
35+
];
36+
37+
/**
38+
* Command structure for JSON format
39+
*/
40+
interface CommandTrigger {
41+
command: string;
42+
args?: unknown[];
43+
}
44+
45+
/**
46+
* Initialize the trigger file handler
47+
* Watches for a trigger file that can auto-execute allowed commands on startup
48+
*/
49+
export function initializeTriggerFileHandler(
50+
context: vscode.ExtensionContext,
51+
): void {
52+
const outputChannel = vscode.window.createOutputChannel("FastEdge Autorun");
53+
context.subscriptions.push(outputChannel);
54+
55+
// Watch for future trigger files only in the root .vscode directory
56+
const workspaceFolders = vscode.workspace.workspaceFolders;
57+
if (!workspaceFolders) {
58+
return;
59+
}
60+
61+
for (const folder of workspaceFolders) {
62+
const pattern = new vscode.RelativePattern(folder, TRIGGER_FILE_PATH);
63+
const watcher = vscode.workspace.createFileSystemWatcher(pattern);
64+
65+
watcher.onDidCreate(async (uri) => {
66+
await executeTriggerFile(uri, outputChannel);
67+
});
68+
69+
watcher.onDidChange(async (uri) => {
70+
await executeTriggerFile(uri, outputChannel);
71+
});
72+
73+
context.subscriptions.push(watcher);
74+
}
75+
}
76+
77+
/**
78+
* Check for existing trigger file on activation
79+
*/
80+
async function checkForTriggerFile(
81+
outputChannel: vscode.OutputChannel,
82+
): Promise<void> {
83+
const workspaceFolders = vscode.workspace.workspaceFolders;
84+
85+
if (!workspaceFolders || workspaceFolders.length === 0) {
86+
outputChannel.appendLine(
87+
"No workspace folder found, skipping trigger file check",
88+
);
89+
return;
90+
}
91+
92+
// Check first workspace folder (most common case)
93+
const triggerPath = vscode.Uri.joinPath(
94+
workspaceFolders[0].uri,
95+
TRIGGER_FILE_PATH,
96+
);
97+
98+
try {
99+
await vscode.workspace.fs.stat(triggerPath);
100+
outputChannel.appendLine(`Found trigger file at: ${triggerPath.fsPath}`);
101+
await executeTriggerFile(triggerPath, outputChannel);
102+
} catch {
103+
// File doesn't exist, that's fine
104+
outputChannel.appendLine("No trigger file found on activation");
105+
}
106+
}
107+
108+
/**
109+
* Execute command from trigger file
110+
*/
111+
async function executeTriggerFile(
112+
uri: vscode.Uri,
113+
outputChannel: vscode.OutputChannel,
114+
): Promise<void> {
115+
try {
116+
outputChannel.appendLine(`\nProcessing trigger file: ${uri.fsPath}`);
117+
118+
// Read file content
119+
const content = await vscode.workspace.fs.readFile(uri);
120+
const contentStr = content.toString().trim();
121+
122+
if (!contentStr) {
123+
outputChannel.appendLine("Trigger file is empty, ignoring");
124+
await vscode.workspace.fs.delete(uri);
125+
return;
126+
}
127+
128+
// Parse command (supports both string and JSON format)
129+
let commandId: string;
130+
let commandArgs: any[] | undefined;
131+
132+
try {
133+
const parsed: CommandTrigger = JSON.parse(contentStr);
134+
commandId = parsed.command;
135+
commandArgs = parsed.args;
136+
outputChannel.appendLine(
137+
`Parsed JSON command: ${commandId} with args: ${JSON.stringify(commandArgs)}`,
138+
);
139+
} catch {
140+
// Not JSON, treat as simple command string
141+
commandId = contentStr;
142+
outputChannel.appendLine(`Parsed simple command: ${commandId}`);
143+
}
144+
145+
// Validate command is in allowlist
146+
if (!ALLOWED_COMMANDS.includes(commandId)) {
147+
const errorMsg = `Command '${commandId}' is not in the allowlist. Allowed commands: ${ALLOWED_COMMANDS.join(", ")}`;
148+
outputChannel.appendLine(`ERROR: ${errorMsg}`);
149+
vscode.window.showErrorMessage(`FastEdge Autorun: ${errorMsg}`);
150+
await vscode.workspace.fs.delete(uri);
151+
return;
152+
}
153+
154+
// Execute command with timeout protection
155+
outputChannel.appendLine(`Executing command: ${commandId}`);
156+
let timeoutHandle: NodeJS.Timeout | undefined;
157+
const timeoutPromise = new Promise((_, reject) => {
158+
timeoutHandle = setTimeout(
159+
() => reject(new Error("Command execution timeout")),
160+
30000,
161+
);
162+
});
163+
164+
const executePromise = commandArgs
165+
? vscode.commands.executeCommand(commandId, ...commandArgs)
166+
: vscode.commands.executeCommand(commandId);
167+
168+
try {
169+
await Promise.race([executePromise, timeoutPromise]);
170+
} finally {
171+
if (timeoutHandle) {
172+
clearTimeout(timeoutHandle);
173+
}
174+
}
175+
176+
outputChannel.appendLine(`✓ Command executed successfully: ${commandId}`);
177+
vscode.window.showInformationMessage(
178+
`FastEdge: Auto-executed '${commandId}'`,
179+
);
180+
181+
// Delete trigger file after successful execution
182+
await vscode.workspace.fs.delete(uri);
183+
outputChannel.appendLine(`Deleted trigger file: ${uri.fsPath}`);
184+
} catch (error) {
185+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
186+
outputChannel.appendLine(
187+
`ERROR: Failed to execute trigger file: ${errorMsg}`,
188+
);
189+
vscode.window.showErrorMessage(`FastEdge Autorun failed: ${errorMsg}`);
190+
191+
// Still try to delete the file to prevent retry loops
192+
try {
193+
await vscode.workspace.fs.delete(uri);
194+
outputChannel.appendLine(`Deleted trigger file after error`);
195+
} catch (deleteError) {
196+
outputChannel.appendLine(
197+
`WARNING: Could not delete trigger file: ${deleteError}`,
198+
);
199+
}
200+
}
201+
}

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
runFile,
1212
runWorkspace,
1313
} from "./commands";
14+
import { initializeTriggerFileHandler } from "./autorun/triggerFileHandler";
1415

1516
export function activate(context: vscode.ExtensionContext) {
1617
// Read the cliVersion from METADATA.json
@@ -30,6 +31,9 @@ export function activate(context: vscode.ExtensionContext) {
3031
vscode.ConfigurationTarget.Global,
3132
);
3233

34+
// Initialize trigger file handler for auto-running commands
35+
initializeTriggerFileHandler(context);
36+
3337
context.subscriptions.push(
3438
vscode.commands.registerCommand("fastedge.run-file", runFile),
3539
vscode.commands.registerCommand("fastedge.run-workspace", runWorkspace),

0 commit comments

Comments
 (0)