Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fddcd14
Setup of the remoteRunner plugin
proto-aiken-13 Jun 15, 2026
bfcea05
Update directory
proto-aiken-13 Jun 17, 2026
d0883bd
Modify the remoteRunnerPlugin structure and add ev3Engine usage
proto-aiken-13 Jun 22, 2026
04bb38f
fix: migrate remoteExecution to vitest, remove abstract, fix rollup c…
Akshay-2007-1 Jun 22, 2026
e9a67bd
chore: add py-slang and transitive deps to yarn.lock
Akshay-2007-1 Jun 22, 2026
806e6c2
chore: fix yarn.lock — merge @babel/runtime range, add @sourceacademy…
Akshay-2007-1 Jun 22, 2026
b713b50
chore: fix yarn.lock ordering and missing entries
Akshay-2007-1 Jun 22, 2026
75af89b
chore: fix extra blank lines in yarn.lock
Akshay-2007-1 Jun 22, 2026
3c206eb
chore: remove trailing blank line from yarn.lock
Akshay-2007-1 Jun 22, 2026
ec51966
fix: disable verbatimModuleSyntax for remoteExecution package
Akshay-2007-1 Jun 22, 2026
549c5b3
fix: set es2020 target and noImplicitAny:false for py-slang compatibi…
Akshay-2007-1 Jun 22, 2026
90026d3
fix: use noCheck:true to skip type-checking py-slang source
Akshay-2007-1 Jun 22, 2026
19fd917
fix: remove unnecessary re-export of remoteRunnerPlugin from runner-test
Akshay-2007-1 Jun 22, 2026
3d467f1
fix: resolve ESLint errors in remoteExecution plugin
Akshay-2007-1 Jun 22, 2026
de8bd92
fix: add .ts extensions to nodeResolve in remoteExecution rollup config
Akshay-2007-1 Jun 22, 2026
08b2202
fix: include py-slang source in TypeScript program for rollup bundling
Akshay-2007-1 Jun 22, 2026
00bce74
fix: add inline plugin to transpile node_modules TypeScript via trans…
Akshay-2007-1 Jun 22, 2026
a04d4be
fix: add @rollup/plugin-commonjs to handle moo/nearley CJS modules
Akshay-2007-1 Jun 22, 2026
2b1b555
fix: add @rollup/plugin-commonjs dep and update yarn.lock workspace e…
Akshay-2007-1 Jun 22, 2026
5a62899
fix: make py-slang external in bundle, inline it via vitest
Akshay-2007-1 Jun 22, 2026
575669d
style: fix Prettier formatting in remoteExecution rollup.config.mjs
Akshay-2007-1 Jun 22, 2026
f979647
style: drop arrow function parens per Prettier arrowParens:avoid setting
Akshay-2007-1 Jun 22, 2026
c318438
test: mock py-slang in runner test to avoid TypeScript source loading
Akshay-2007-1 Jun 22, 2026
572ea84
fix: use Promise.resolve instead of async to satisfy require-await li…
Akshay-2007-1 Jun 22, 2026
5a257b0
feat(ev3): add conductor-based EV3 evaluator bundle
proto-aiken-13 Jun 29, 2026
764ee5b
Merge branch 'main' into branch-remoteRunnerPlugin-fixed
proto-aiken-13 Jun 29, 2026
a9f681f
refactor(remoteExecution): make plugin transport-only, remove py-slan…
proto-aiken-13 Jul 5, 2026
960563e
Merge branch 'main' into branch-remoteRunnerPlugin-fixed
proto-aiken-13 Jul 5, 2026
cbfc00e
Update rollup.config.mjs
proto-aiken-13 Jul 5, 2026
9121847
Simplify ev3 plugin design to remove py-slang dependencies
proto-aiken-13 Jul 6, 2026
912589b
Merge branch 'branch-remoteRunnerPlugin-fixed' of https://github.com/…
proto-aiken-13 Jul 6, 2026
f3b30c0
Update yarn.lock
proto-aiken-13 Jul 6, 2026
f32084b
Prettified remoteExecution plugin files
proto-aiken-13 Jul 6, 2026
a122ac3
fix(remote-execution): unblock PR CI - add missing common-remote-exec…
Akshay-2007-1 Sep 7, 2026
3143293
Merge remote-tracking branch 'origin/main' into remote-runner-plugin-…
Akshay-2007-1 Sep 7, 2026
e18a829
Fix yarn.lock drift from merge - commit the actually-resolved lockfile
Akshay-2007-1 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@
"bugs": {
"url": "https://github.com/source-academy/plugins/issues"
},
"homepage": "https://github.com/source-academy/plugins#readme"
"homepage": "https://github.com/source-academy/plugins#readme",
"resolutions": {
"@sourceacademy/conductor": "^0.5.0"
}
}
3 changes: 3 additions & 0 deletions src/common/remoteExecution/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "installable"
}
33 changes: 33 additions & 0 deletions src/common/remoteExecution/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@sourceacademy/common-remote-execution",
"version": "0.0.1",
"packageManager": "yarn@4.6.0",
"description": "Shared types/constants for the transport-only remote-execution runner plugin and its web-side counterpart",
"scripts": {
"build": "rollup -c",
"prepack": "yarn build"
},
"license": "ISC",
"files": [
"dist"
],
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^12.3.0",
"rollup": "^4.60.2",
"tslib": "^2.8.1",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
}
}
21 changes: 21 additions & 0 deletions src/common/remoteExecution/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import nodeResolve from "@rollup/plugin-node-resolve";
import terser from "@rollup/plugin-terser";
import typescript from "@rollup/plugin-typescript";

/**
* @type {import('rollup').RollupOptions}
*/
export default {
input: "src/index.ts",
output: [
{
file: "dist/index.cjs",
format: "cjs",
},
{
file: "dist/index.mjs",
format: "esm",
},
],
plugins: [nodeResolve(), typescript(), terser()],
};
18 changes: 18 additions & 0 deletions src/common/remoteExecution/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const WEB_ID = "__web_remote_execution";
export const RUNNER_ID = "__runner_remote_execution";

export const CHANNEL_ID = "remote-execution";

export const MESSAGE_TYPE_CONNECTION_STATUS = "connectionStatus";

/**
* Mirrors the existing `DeviceConnection['status']` union from the frontend's
* (non-conductor) remoteExecution feature, so a web-side consumer can reuse
* the same state machine it already has for the legacy connection flow.
*/
export type ConnectionStatus = "CONNECTING" | "CONNECTED" | "FAILED";

export type ConnectionStatusMessage = {
type: typeof MESSAGE_TYPE_CONNECTION_STATUS;
status: ConnectionStatus;
};
10 changes: 10 additions & 0 deletions src/common/remoteExecution/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"exclude": ["./dist"],
"include": ["./src"],
"compilerOptions": {
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
}
}
2 changes: 1 addition & 1 deletion src/common/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"exclude": ["./dist"],
"include": ["./src"],
"include": ["./src", "../../runner/test/src/__tests__/runner.test.ts"],
"compilerOptions": {
"declaration": true,
"outDir": "./dist",
Expand Down
26 changes: 2 additions & 24 deletions src/runner/remoteExecution/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,2 @@
import { CHANNEL_ID, RUNNER_ID, type PySlangMessage } from "@sourceacademy/common-test";
import type { IPlugin, IChannel, IConduit } from "@sourceacademy/conductor/conduit";
import { EV3Engine } from "py-slang/src/engines/ev3/EV3Engine";

export class remoteRunnerPlugin implements IPlugin {
readonly id: string = RUNNER_ID;
static readonly channelAttach = [CHANNEL_ID];
private readonly __channel: IChannel<PySlangMessage>;
private readonly engine: EV3Engine;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(_conduit: IConduit, [channel]: IChannel<any>[]) {
this.__channel = channel;
this.engine = new EV3Engine();

this.__channel.subscribe(async message => {
if (message.type === "run") {
const result = await this.engine.execute(message.code);
console.log("Engine response:", result);
this.__channel.send({ type: "result", output: JSON.stringify(result) });
}
});
}
}
export { RemoteExecutionPlugin } from "./src";
export type { ConnectionStatus, ConnectionStatusMessage } from "./src";
10 changes: 6 additions & 4 deletions src/runner/remoteExecution/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@
"build": "rollup -c",
"prepack": "yarn build"
},
"dependencies": {
"py-slang": "github:proto-aiken-13/py-slang#branch-ev3-engine"
},
"peerDependencies": {
"@sourceacademy/common-remote-execution": "workspace:*",
"@sourceacademy/conductor": "^0.8.0"
},
Comment thread
Akshay-2007-1 marked this conversation as resolved.
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.3",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^12.3.0",
"@sourceacademy/common-remote-execution": "workspace:*",
"@sourceacademy/common-test": "workspace:*",
"@sourceacademy/conductor": "^0.8.0",
"@vitest/coverage-istanbul": "^4.1.9",
"rollup": "^4.60.2",
"esbuild": "^0.28.1",
"rollup": "^4.62.2",
"rollup-plugin-esbuild": "^6.2.1",
Comment thread
Akshay-2007-1 marked this conversation as resolved.
"tslib": "^2.8.1",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
Expand Down
28 changes: 10 additions & 18 deletions src/runner/remoteExecution/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,13 @@ import nodeResolve from "@rollup/plugin-node-resolve";
import terser from "@rollup/plugin-terser";
import typescript from "@rollup/plugin-typescript";

/**
* @type {import('rollup').RollupOptions}
*/
export default {
input: "src/index.ts",
external: id => id.includes("py-slang"),
output: [
{
file: "dist/index.cjs",
format: "cjs",
},
{
file: "dist/index.mjs",
format: "esm",
},
],
plugins: [nodeResolve(), typescript(), terser()],
};
export default [
{
input: "src/index.ts",
output: [
{ file: "dist/index.cjs", format: "cjs" },
{ file: "dist/index.mjs", format: "esm" },
],
plugins: [nodeResolve(), typescript(), terser()],
},
];
26 changes: 25 additions & 1 deletion src/runner/remoteExecution/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
export { remoteRunnerPlugin } from "../index";
import {
CHANNEL_ID,
RUNNER_ID,
MESSAGE_TYPE_CONNECTION_STATUS,
type ConnectionStatusMessage,
type ConnectionStatus,
} from "@sourceacademy/common-remote-execution";
import type { IPlugin, IChannel, IConduit } from "@sourceacademy/conductor/conduit";

export class RemoteExecutionPlugin implements IPlugin {
readonly id: string = RUNNER_ID;
static readonly channelAttach = [CHANNEL_ID];
private readonly __channel: IChannel<ConnectionStatusMessage>;

constructor(_conduit: IConduit, [channel]: IChannel<ConnectionStatusMessage>[]) {
if (!channel) {
throw new Error("Remote execution channel is required but was not provided.");
}
this.__channel = channel;
}

sendConnectionStatus(status: ConnectionStatus): void {
this.__channel.send({ type: MESSAGE_TYPE_CONNECTION_STATUS, status });
}
}
4 changes: 1 addition & 3 deletions src/runner/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^12.3.0",
"@sourceacademy/common-remote-execution": "workspace:*",
"@sourceacademy/common-test": "workspace:*",
"@sourceacademy/conductor": "^0.8.0",
"@sourceacademy/runner-remote-execution": "workspace:*",
Expand All @@ -36,8 +37,5 @@
"tslib": "^2.8.1",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
},
"dependencies": {
"py-slang": "github:proto-aiken-13/py-slang#branch-ev3-engine"
}
}
45 changes: 19 additions & 26 deletions src/runner/test/src/__tests__/runner.test.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
import { vi, test, expect } from "vitest";
import { RemoteExecutionPlugin } from "@sourceacademy/runner-remote-execution";
import type { ConnectionStatusMessage } from "@sourceacademy/common-remote-execution";

vi.mock("py-slang/src/engines/ev3/EV3Engine", () => ({
EV3Engine: class MockEV3Engine {
execute(code: string) {
return Promise.resolve({ output: `mock: ${code}` });
}
},
}));

import { remoteRunnerPlugin } from "@sourceacademy/runner-remote-execution";
import type { PySlangMessage } from "@sourceacademy/common-test";

test("plugin subscribes to channel and sends result on run message", async () => {
const sentMessages: PySlangMessage[] = [];
let messageHandler: (msg: PySlangMessage) => void = () => {};
test("plugin forwards connection status over its channel without touching execution", () => {
const sentMessages: ConnectionStatusMessage[] = [];

const mockChannel = {
name: "py_slang_channel",
send: (msg: PySlangMessage) => {
name: "remote-execution",
send: (msg: ConnectionStatusMessage) => {
sentMessages.push(msg);
},
subscribe: (handler: (msg: PySlangMessage) => void) => {
messageHandler = handler;
},
unsubscribe: () => {},
close: () => {},
subscribe: vi.fn(),
unsubscribe: vi.fn(),
close: vi.fn(),
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockConduit = {} as any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new remoteRunnerPlugin(mockConduit, [mockChannel as any]);
const plugin = new RemoteExecutionPlugin(mockConduit, [mockChannel]);

plugin.sendConnectionStatus("CONNECTED");

await messageHandler({ type: "run", code: "1 + 1" });
expect(sentMessages).toStrictEqual([{ type: "connectionStatus", status: "CONNECTED" }]);
});

test("plugin construction throws if no channel is attached", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockConduit = {} as any;

expect(sentMessages.length).toBeGreaterThan(0);
expect(sentMessages[0]).toMatchObject({ type: "result" });
expect(() => new RemoteExecutionPlugin(mockConduit, [])).toThrow();
});
Loading
Loading