mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[hAIk]: refactor backend settings and tools routes to use explicit endpoint names (/get_settings, /update_settings, /get_builtin_permissions, /update_builtin_permissions) with matching frontend updates; simplify base_routes.ts to relative /api path; add new appsSlice Redux slice and register in store; remove unused GET_HISTORY reducer; rename structlint to lint across CI workflow, local.sh, and publish.sh; strip ANSI escape codes from NineRouter forwarded output; add swarm-debug as backend dependency
This commit is contained in:
@@ -15,7 +15,7 @@ jobs:
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- run: python3 linter/structlint.py --root .
|
||||
- run: python3 linter/lint.py --root .
|
||||
|
||||
typecheck:
|
||||
name: Type check (backend)
|
||||
|
||||
@@ -30,12 +30,12 @@ def load_settings() -> AppSettings:
|
||||
return AppSettings()
|
||||
|
||||
|
||||
@settings.router.get("")
|
||||
@settings.router.get("/get_settings")
|
||||
async def get_settings():
|
||||
return load_settings().model_dump()
|
||||
|
||||
|
||||
@settings.router.put("")
|
||||
@settings.router.put("/update_settings")
|
||||
async def update_settings(body: AppSettings):
|
||||
os.makedirs(SETTINGS_DIR, exist_ok=True)
|
||||
with open(SETTINGS_FILE, "w") as f:
|
||||
|
||||
+5
-1
@@ -1,12 +1,16 @@
|
||||
import re
|
||||
|
||||
from typeguard import typechecked
|
||||
|
||||
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]|\x1b[c78]")
|
||||
|
||||
|
||||
# TODO: type spec this entirely
|
||||
@typechecked
|
||||
def forward_output(pipe) -> None:
|
||||
try:
|
||||
for line in iter(pipe.readline, b""):
|
||||
text = line.decode("utf-8", errors="replace").rstrip()
|
||||
text = _ANSI_RE.sub("", line.decode("utf-8", errors="replace")).rstrip()
|
||||
if text:
|
||||
print(f"[9router] {text}", flush=True)
|
||||
except Exception:
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies = [
|
||||
"posthog",
|
||||
"pydantic==2.10.5",
|
||||
"watchfiles",
|
||||
"swarm-debug"
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
|
||||
Generated
+19
@@ -885,6 +885,7 @@ dependencies = [
|
||||
{ name = "pillow" },
|
||||
{ name = "posthog" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "swarm-debug" },
|
||||
{ name = "watchfiles" },
|
||||
]
|
||||
|
||||
@@ -907,6 +908,7 @@ requires-dist = [
|
||||
{ name = "pillow" },
|
||||
{ name = "posthog" },
|
||||
{ name = "pydantic", specifier = "==2.10.5" },
|
||||
{ name = "swarm-debug" },
|
||||
{ name = "watchfiles" },
|
||||
]
|
||||
|
||||
@@ -1705,6 +1707,23 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swarm-debug"
|
||||
version = "0.1.47"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "packaging" },
|
||||
{ name = "rich" },
|
||||
{ name = "typeguard" },
|
||||
{ name = "typer" },
|
||||
{ name = "uvicorn", extra = ["standard"] },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/9c/bd571dea78033dfea387f626de983115b8a96d9dd3478166119f38a115e3/swarm_debug-0.1.47.tar.gz", hash = "sha256:b3261dd675f3b4ca1046a6d293169e0dcb3f827660a2c747103b527bbb0e712c", size = 397465, upload-time = "2026-04-17T00:48:29.047Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/16/62716f8f0cdd9e4e3ff75ed4ae034dbfcf3a349d65a1fb1f3894bb686c11/swarm_debug-0.1.47-py3-none-any.whl", hash = "sha256:c137635a1425adfddc8147766c62dd2e7f811e4911682bcdacce496144a56d0a", size = 399393, upload-time = "2026-04-17T00:48:27.469Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface AppSettings {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
const get_settings_endpoint: string = SETTINGS_API;
|
||||
const get_settings_endpoint: string = `${SETTINGS_API}/get_settings`;
|
||||
async function get_settings_function(): Promise<AppSettings> {
|
||||
const res = await fetch(get_settings_endpoint, {
|
||||
method: 'GET',
|
||||
@@ -55,7 +55,7 @@ export const GET_SETTINGS = createAsyncThunk(
|
||||
);
|
||||
|
||||
|
||||
const update_settings_endpoint: string = SETTINGS_API;
|
||||
const update_settings_endpoint: string = `${SETTINGS_API}/update_settings`;
|
||||
async function update_settings_function(body: Partial<AppSettings>): Promise<{ ok: boolean; settings: AppSettings }> {
|
||||
const res = await fetch(update_settings_endpoint, {
|
||||
method: 'PUT',
|
||||
|
||||
@@ -23,7 +23,7 @@ export const LIST_BUILTIN_TOOLS = createAsyncThunk(
|
||||
);
|
||||
|
||||
|
||||
const get_builtin_permissions_endpoint: string = `${TOOLS_API}/builtin/permissions`;
|
||||
const get_builtin_permissions_endpoint: string = `${TOOLS_API}/get_builtin_permissions`;
|
||||
async function get_builtin_permissions_function(): Promise<{ permissions: Record<string, string> }> {
|
||||
const res = await fetch(get_builtin_permissions_endpoint, {
|
||||
method: 'GET',
|
||||
@@ -38,7 +38,7 @@ export const GET_BUILTIN_PERMISSIONS = createAsyncThunk(
|
||||
);
|
||||
|
||||
|
||||
const update_builtin_permissions_endpoint: string = `${TOOLS_API}/builtin/permissions`;
|
||||
const update_builtin_permissions_endpoint: string = `${TOOLS_API}/update_builtin_permissions`;
|
||||
async function update_builtin_permissions_function(permissions: Record<string, string>): Promise<{ permissions: Record<string, string> }> {
|
||||
const res = await fetch(update_builtin_permissions_endpoint, {
|
||||
method: 'PUT',
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
const port: number = (window as any).__OPENSWARM_PORT__ || 8325;
|
||||
const host: string = window.location.hostname || 'localhost';
|
||||
|
||||
export const API_BASE: string = `http://${host}:${port}/api`;
|
||||
export const WS_BASE: string = `ws://${host}:${port}`;
|
||||
export const API_BASE: string = `/api`;
|
||||
@@ -173,11 +173,6 @@ export function buildExtraReducers(builder: ActionReducerMapBuilder<AgentsState>
|
||||
state.expandedSessionIds = state.expandedSessionIds.filter((id) => id !== sessionId);
|
||||
state.trackedNotificationIds = state.trackedNotificationIds.filter((id) => id !== sessionId);
|
||||
})
|
||||
.addCase(GET_HISTORY.fulfilled, (state, action) => {
|
||||
const history: Record<string, HistorySession> = {};
|
||||
for (const s of action.payload.sessions) history[s.id] = s;
|
||||
state.history = history;
|
||||
})
|
||||
.addCase(RESUME_SESSION.fulfilled, (state, action) => {
|
||||
const session = action.payload;
|
||||
state.sessions[session.id] = { ...session, streamingMessage: null, tool_group_meta: session.tool_group_meta ?? {} };
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import {
|
||||
LIST_APPS,
|
||||
CREATE_APP,
|
||||
UPDATE_APP,
|
||||
DELETE_APP,
|
||||
GET_APP,
|
||||
} from '@/shared/backend-bridge/apps/app_builder';
|
||||
import type { App } from '@/shared/backend-bridge/apps/app_builder';
|
||||
|
||||
interface AppsState {
|
||||
items: Record<string, App>;
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
const initialState: AppsState = {
|
||||
items: {},
|
||||
loading: false,
|
||||
loaded: false,
|
||||
};
|
||||
|
||||
const appsSlice = createSlice({
|
||||
name: 'apps',
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(LIST_APPS.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(LIST_APPS.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.loaded = true;
|
||||
const items: Record<string, App> = {};
|
||||
for (const a of action.payload.apps) {
|
||||
items[a.id] = a;
|
||||
}
|
||||
state.items = items;
|
||||
})
|
||||
.addCase(LIST_APPS.rejected, (state) => {
|
||||
state.loading = false;
|
||||
state.loaded = true;
|
||||
})
|
||||
.addCase(CREATE_APP.fulfilled, (state, action) => {
|
||||
state.items[action.payload.id] = action.payload;
|
||||
})
|
||||
.addCase(UPDATE_APP.fulfilled, (state, action) => {
|
||||
const a = action.payload;
|
||||
if (a?.id) {
|
||||
state.items[a.id] = { ...state.items[a.id], ...a };
|
||||
}
|
||||
})
|
||||
.addCase(DELETE_APP.fulfilled, (state, action) => {
|
||||
delete state.items[action.payload];
|
||||
})
|
||||
.addCase(GET_APP.fulfilled, (state, action) => {
|
||||
const a = action.payload;
|
||||
if (a?.id) {
|
||||
state.items[a.id] = a;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default appsSlice.reducer;
|
||||
@@ -9,6 +9,7 @@ import settingsReducer from './settingsSlice';
|
||||
import skillRegistryReducer from './skillRegistrySlice';
|
||||
import dashboardLayoutReducer from './dashboardLayoutSlice';
|
||||
import dashboardsReducer from './dashboardsSlice';
|
||||
import appsReducer from './appsSlice';
|
||||
import updateReducer from './updateSlice';
|
||||
// import modelsReducer from './modelsSlice';
|
||||
|
||||
@@ -24,6 +25,7 @@ export const store = configureStore({
|
||||
skillRegistry: skillRegistryReducer,
|
||||
dashboardLayout: dashboardLayoutReducer,
|
||||
dashboards: dashboardsReducer,
|
||||
apps: appsReducer,
|
||||
update: updateReducer,
|
||||
// models: modelsReducer,
|
||||
},
|
||||
|
||||
+2
-2
@@ -90,7 +90,7 @@ BACKEND_PORT=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/port
|
||||
FRONTEND_PORT=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/ports.config.json'))['frontend']['dev'])")
|
||||
|
||||
# --- Run structural linter (warnings only, non-blocking) ---
|
||||
LINT_OUTPUT=$(python3 "$PROJECT_ROOT/linter/structlint.py" --root "$PROJECT_ROOT" 2>&1)
|
||||
LINT_OUTPUT=$(python3 "$PROJECT_ROOT/linter/lint.py" --root "$PROJECT_ROOT" 2>&1)
|
||||
LINT_EXIT=$?
|
||||
if [ $LINT_EXIT -ne 0 ]; then
|
||||
echo ""
|
||||
@@ -99,7 +99,7 @@ if [ $LINT_EXIT -ne 0 ]; then
|
||||
echo -e "${YELLOW} $line${RESET}"
|
||||
done
|
||||
LINT_COUNT=$(echo "$LINT_OUTPUT" | grep -oE '[0-9]+ error' | head -1 | grep -oE '[0-9]+')
|
||||
echo -e "${YELLOW}${BOLD} ${LINT_COUNT} violation(s) — fix or add exceptions in linter/structlint.json${RESET}"
|
||||
echo -e "${YELLOW}${BOLD} ${LINT_COUNT} violation(s) — fix or add exceptions in linter/config/config.json${RESET}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
|
||||
+2
-2
@@ -13,9 +13,9 @@ PROJECT_ROOT="$(dirname "$RUN_DIR_ROOT")"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "Running structural linter..."
|
||||
if ! python3 "$PROJECT_ROOT/linter/structlint.py" --root "$PROJECT_ROOT"; then
|
||||
if ! python3 "$PROJECT_ROOT/linter/lint.py" --root "$PROJECT_ROOT"; then
|
||||
echo ""
|
||||
echo "Publish blocked: structlint found violations. Fix them or add exceptions in linter/structlint.json."
|
||||
echo "Publish blocked: linter found violations. Fix them or add exceptions in linter/config/config.json."
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user