Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ trim_trailing_whitespace = true
# Markdown files can have flexible indentation for lists
[*.md]
indent_size = unset

[.mise.toml]
max_line_length = off

[*.dart]
max_line_length = off
17 changes: 16 additions & 1 deletion .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ action-validator = "latest"
"chromedriver" = "146"
cmake = "latest"
codex = "latest"
dart = { version = "latest", url = "https://storage.googleapis.com/dart-archive/channels/stable/release/{{ version }}/sdk/dartsdk-{{ os() }}-{{ arch() }}-release.zip", version_expr = 'fromJSON(body).prefixes | filter({ # matches "^channels/stable/release/(\\d+\\.\\d+\\.\\d+)/$" }) | map({split(#, "/")[3]}) | sortVersions()', version_list_url = "https://storage.googleapis.com/storage/v1/b/dart-archive/o?prefix=channels/stable/release/&delimiter=/" }
dprint = "latest"
editorconfig-checker = "latest"
gemini-cli = "latest"
Expand All @@ -22,6 +23,7 @@ typos = "latest"
uv = "latest"

[tasks]
ec = "ec"
editorconfig-check = "ec"
ruff-check = "ruff check services/ws-modules/"
ruff-fmt = "ruff format services/ws-modules/"
Expand All @@ -36,17 +38,24 @@ DPRINT_CACHE_DIR = "/tmp/dprint-cache"
run = "dprint fmt"

[tasks.fmt]
depends = ["cargo-clippy-fix", "cargo-fmt", "dprint-fmt", "ruff-fmt", "taplo-fmt"]
depends = ["cargo-clippy-fix", "cargo-fmt", "dart-fmt", "dprint-fmt", "ruff-fmt", "taplo-fmt"]
description = "Run repository formatters"

[tasks.install-nightly]
run = "cargo +nightly fmt --version >/dev/null 2>&1 || rustup toolchain install nightly --component rustfmt"

[tasks.dart-check]
run = "dart analyze services/ws-modules/dart-comm1/"

[tasks.dart-fmt]
run = "dart format services/ws-modules/dart-comm1/"

[tasks.check]
depends = [
"cargo-check",
"cargo-clippy",
"cargo-fmt-check",
"dart-check",
"dprint-check",
"editorconfig-check",
"osv-scanner",
Expand Down Expand Up @@ -164,6 +173,11 @@ description = "Build the nfc workflow WASM module"
dir = "services/ws-modules/nfc"
run = "wasm-pack build . --target web"

[tasks.build-ws-dart-comm1-module]
description = "Build the dart-comm1 workflow module"
dir = "services/ws-modules/dart-comm1"
run = "dart compile js lib/dart_comm1.dart -o pkg/et_ws_dart_comm1_compiled.js --no-source-maps"

[tasks.build-ws-pydata1-module]
description = "Build the pydata1 Python workflow module"
dir = "services/ws-modules/pydata1"
Expand All @@ -177,6 +191,7 @@ depends = [
"build-ws-audio1-module",
"build-ws-bluetooth-module",
"build-ws-comm1-module",
"build-ws-dart-comm1-module",
"build-ws-data1-module",
"build-ws-face-detection-module",
"build-ws-geolocation-module",
Expand Down
7 changes: 7 additions & 0 deletions services/ws-modules/dart-comm1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
155 changes: 155 additions & 0 deletions services/ws-modules/dart-comm1/lib/dart_comm1.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import 'dart:async';
import 'dart:js_interop';

// JS interop declarations for et_ws_wasm_agent
@JS()
extension type WsClientConfig._(JSObject _) implements JSObject {
external factory WsClientConfig(String serverUrl);
}

@JS()
extension type WsClient._(JSObject _) implements JSObject {
external factory WsClient(WsClientConfig config);
external void connect();
external void disconnect();
// ignore: non_constant_identifier_names
external String get_state();
// ignore: non_constant_identifier_names
external String get_client_id();
external void send(String message);
// ignore: non_constant_identifier_names
external void set_on_message(JSFunction callback);
}

// JS interop for browser globals
@JS('window.location.protocol')
external String get locationProtocol;

@JS('window.location.host')
external String get locationHost;

@JS('document.getElementById')
external JSObject? getElementById(String id);

@JS()
extension type _TextArea._(JSObject _) implements JSObject {
external String get value;
external set value(String v);
}

void appendOutput(String msg) {
final el = getElementById('module-output');
if (el != null) {
final ta = el as _TextArea;
ta.value = ta.value.isEmpty ? msg : '${ta.value}\n$msg';
}
}

void log(String msg) {
appendOutput('[dart-comm1] $msg');
}

String get wsUrl {
final proto = locationProtocol == 'https:' ? 'wss:' : 'ws:';
return '$proto//$locationHost/ws';
}

Future<void> sleep(int ms) {
final c = Completer<void>();
Timer(Duration(milliseconds: ms), c.complete);
return c.future;
}

Future<void> waitForConnected(WsClient client) async {
for (var i = 0; i < 100; i++) {
if (client.get_state() == 'connected') return;
await sleep(100);
}
throw Exception('Timeout waiting for WebSocket connection');
}

Future<String> waitForAgentId(WsClient client) async {
for (var i = 0; i < 100; i++) {
final id = client.get_client_id();
if (id.isNotEmpty) return id;
await sleep(100);
}
throw Exception('Timeout waiting for agent_id');
}

Future<void> run() async {
log('entered run()');

final client = WsClient(WsClientConfig(wsUrl));

String selfAgentId = '';
String? targetAgentId;

client.set_on_message(
((JSString raw) {
final data = raw.toDart;
try {
// Parse type field manually to avoid a JSON dep
if (data.contains('"list_agents_response"')) {
// Extract first other connected agent id
final idMatches = RegExp(
r'"agent_id"\s*:\s*"([^"]+)"',
).allMatches(data);
for (final m in idMatches) {
final id = m.group(1)!;
if (id != selfAgentId) {
targetAgentId = id;
break;
}
}
} else if (data.contains('"agent_message"') ||
data.contains('"message_status"')) {
log('received: $data');
appendOutput(data);
}
} catch (_) {}
}).toJS,
);

client.connect();
await waitForConnected(client);
selfAgentId = await waitForAgentId(client);
log('connected as $selfAgentId');

// Poll for a peer agent
while (targetAgentId == null) {
client.send('{"type":"list_agents"}');
await sleep(1000);
}

log('found peer $targetAgentId, sending broadcast');
client.send(
'{"type":"broadcast_message","message":{"module":"dart-comm1","step":"broadcast","from_agent_id":"$selfAgentId","message":"dart-comm1 broadcast to all other connected agents"}}',
);

await sleep(3000);

log('sending direct message to $targetAgentId');
client.send(
'{"type":"send_agent_message","to_agent_id":"$targetAgentId","message":{"module":"dart-comm1","step":"direct","from_agent_id":"$selfAgentId","message":"dart-comm1 direct message"}}',
);

await sleep(3000);
client.disconnect();
log('workflow complete');
}

@JS('dartComm1Run')
external set _dartComm1Run(JSFunction f);

void main() {
_dartComm1Run = (() {
return (() async {
try {
await run();
} catch (e, st) {
throw '$e\n$st'.toJS;
}
}().toJS);
}.toJS);
}
1 change: 1 addition & 0 deletions services/ws-modules/dart-comm1/pkg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*_compiled*
34 changes: 34 additions & 0 deletions services/ws-modules/dart-comm1/pkg/et_ws_dart_comm1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// et_ws_dart_comm1.js — ES module shim for dart-comm1

export default async function init() {
await new Promise((resolve, reject) => {
const s = document.createElement("script");
s.src = new URL("et_ws_dart_comm1_compiled.js", import.meta.url).href;
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}

export async function run() {
if (typeof globalThis.dartComm1Run !== "function") {
throw new Error("dart-comm1: not initialized");
}
// Dart @JS() interop resolves against globalThis, so expose the wasm-agent
// classes there for the duration of the call.
const { WsClient, WsClientConfig } = await import("/pkg/et_ws_wasm_agent.js");
globalThis.WsClient = WsClient;
globalThis.WsClientConfig = WsClientConfig;
try {
const result = globalThis.dartComm1Run();
console.log("dart-comm1 dartComm1Run returned:", result, typeof result);
await result;
} catch (e) {
console.error("dart-comm1 raw error:", e, "boxed:", e?.error);
const msg = e?.error?.toString?.() ?? e?.message ?? String(e);
throw new Error(msg);
} finally {
delete globalThis.WsClient;
delete globalThis.WsClientConfig;
}
}
8 changes: 8 additions & 0 deletions services/ws-modules/dart-comm1/pkg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "et-ws-dart-comm1",
"type": "module",
"description": "dart comm1",
"version": "0.1.0",
"license": "Apache-2.0 OR MIT",
"main": "et_ws_dart_comm1.js"
}
13 changes: 13 additions & 0 deletions services/ws-modules/dart-comm1/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: et_dart_comm1
description: dart-comm1 workflow module
version: 0.1.0
repository: https://github.com/edge-toolkit/core

environment:
sdk: ^3.11.5

dependencies:
web: ^1.1.0

dev_dependencies:
lints: ^6.0.0
Loading