Compare commits

..
Author SHA1 Message Date
dependabot[bot]andGitHub 96eb2ab356 ci(deps): bump the github-actions group with 4 updates
Bumps the github-actions group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [docker/login-action](https://github.com/docker/login-action), [anomalyco/opencode/github](https://github.com/anomalyco/opencode) and [actions/stale](https://github.com/actions/stale).


Updates `actions/checkout` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v7...3d3c42e5aac5ba805825da76410c181273ba90b1)

Updates `docker/login-action` from 4.5.1 to 4.6.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/abd2ef45e78c5afb21d64d4ca52ee8550d9572c7...dbcb813823bdd20940b903addbd779551569679f)

Updates `anomalyco/opencode/github` from 1.18.5 to 1.18.9
- [Release notes](https://github.com/anomalyco/opencode/releases)
- [Commits](https://github.com/anomalyco/opencode/compare/e5cc278dec9294a627a7b05f47ce6a564408c1a2...4da7bb44c84e013fa53e9c5d02ac753d1435c81a)

Updates `actions/stale` from 10.4.0 to 11.0.0
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/1e223db275d687790206a7acac4d1a11bd6fe629...4391f3da665fdf50b6810c1a66712fb9ba21aa93)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: docker/login-action
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: anomalyco/opencode/github
  dependency-version: 1.18.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: 11.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 09:12:59 +00:00
7 changed files with 5 additions and 129 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c #v4.2.0
- name: Log in to Docker Hub
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 #v4.5.1
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f #v4.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+1 -1
View File
@@ -659,7 +659,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
- name: Run opencode
uses: anomalyco/opencode/github@e5cc278dec9294a627a7b05f47ce6a564408c1a2 #v1.18.5
uses: anomalyco/opencode/github@4da7bb44c84e013fa53e9c5d02ac753d1435c81a #v1.18.9
env:
ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
# code. Nothing from the fork is executed in this privileged job; the PR
# is only ever read as text.
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Gather pull request text
env:
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue has been inactive for 30 days. Please respond to keep it open."
-46
View File
@@ -294,14 +294,6 @@ pub struct RunRemoteResponse {
pub status: String,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct StopRemoteResponse {
pub session_id: String,
pub status: String,
/// What the session actually cost, in seconds.
pub billed_seconds: u64,
}
#[derive(Debug, Deserialize, ToSchema)]
struct RunProfileRequest {
url: Option<String>,
@@ -601,7 +593,6 @@ impl ApiServer {
.routes(routes!(get_profile, update_profile, delete_profile))
.routes(routes!(run_profile))
.routes(routes!(run_profile_remote))
.routes(routes!(stop_remote_session))
.routes(routes!(set_profile_cloud_sync))
.routes(routes!(open_url_in_profile))
.routes(routes!(kill_profile))
@@ -2367,43 +2358,6 @@ pub fn remote_launch_precondition(
Ok(())
}
// API Handler - Stop a REMOTE session started by run-remote
#[utoipa::path(
delete,
path = "/v1/remote-sessions/{id}",
params(
("id" = String, Path, description = "Remote session ID from run-remote")
),
responses(
(status = 200, description = "Remote session stopped", body = StopRemoteResponse),
(status = 401, description = "Unauthorized"),
(status = 404, description = "No such remote session"),
(status = 429, description = "Automation request rate limit exceeded"),
(status = 503, description = "The fleet could not be reached; the session is still running"),
(status = 500, description = "Internal server error")
),
security(
("bearer_auth" = [])
),
tag = "profiles"
)]
async fn stop_remote_session(
Path(id): Path<String>,
) -> Result<Json<StopRemoteResponse>, (StatusCode, String)> {
// Without this route, `run-remote` hands back a session id nothing can act
// on: the only thing that ends a session is the fleet's own two-hour cap, so
// every launch bills 7200s no matter how briefly it ran.
let outcome = crate::remote_session::end_remote_session(&id)
.await
.map_err(remote_session_error_response)?;
Ok(Json(StopRemoteResponse {
session_id: outcome.session_id,
status: outcome.status,
billed_seconds: outcome.billed_seconds,
}))
}
fn remote_session_error_response(
err: crate::remote_session::RemoteSessionError,
) -> (StatusCode, String) {
-76
View File
@@ -140,57 +140,6 @@ pub async fn start_remote_session(
.map_err(|e| classify_error_string(&e))
}
/// What the backend returns when a session is stopped.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndRemoteSessionOutcome {
pub session_id: String,
pub status: String,
/// What the session actually cost, in seconds.
pub billed_seconds: u64,
}
/// Ask donutbrowser-infra to stop a remote session.
///
/// Without this the only thing that ends a session is the fleet's own two-hour
/// cap, so every launch bills the full 7200s however briefly it was used — a
/// handful of runs exhausts an allowance meant for a hundred. The backend
/// refuses to retire a row it could not stop on the fleet, so a successful
/// return here means the browser is really down and the profile lock released.
pub async fn end_remote_session(
session_id: &str,
) -> Result<EndRemoteSessionOutcome, RemoteSessionError> {
let endpoint = format!(
"{}/api/remote-sessions/{}",
crate::cloud_auth::CLOUD_API_URL,
urlencoding::encode(session_id)
);
crate::cloud_auth::CLOUD_AUTH
.api_call_with_retry(|token| {
let endpoint = endpoint.clone();
async move {
let response = reqwest::Client::new()
.delete(&endpoint)
.bearer_auth(token)
.send()
.await
.map_err(|e| format!("reach backend: {e}"))?;
let status = response.status().as_u16();
if !(200..300).contains(&status) {
let text = response.text().await.unwrap_or_default();
return Err(format!("({status}) {text}"));
}
response
.json::<EndRemoteSessionOutcome>()
.await
.map_err(|e| format!("decode response: {e}"))
}
})
.await
.map_err(|e| classify_error_string(&e))
}
/// Recover a typed error from `api_call_with_retry`'s string.
///
/// That helper flattens everything to `String` to do its 401 sniffing, so the
@@ -274,31 +223,6 @@ mod tests {
));
}
#[test]
fn the_stop_response_parses_what_the_backend_actually_sends() {
// Pinned against EndRemoteSessionOutcome in donutbrowser-infra
// (apps/backend/src/remote-sessions/remote-sessions.service.ts). A field
// name that does not match makes every stop fail at the decode step, and
// the session then runs to the 2h cap and bills 7200s — the exact defect
// this endpoint exists to fix, reintroduced silently.
let outcome: EndRemoteSessionOutcome =
serde_json::from_str(r#"{"session_id":"sess-1","status":"closed","billed_seconds":42}"#)
.expect("the backend's stop payload must deserialize");
assert_eq!(outcome.session_id, "sess-1");
assert_eq!(outcome.status, "closed");
assert_eq!(outcome.billed_seconds, 42);
}
#[test]
fn a_stop_of_an_unknown_session_is_not_reported_as_capacity() {
// The backend 404s a session that is not the caller's. Mapping that to
// NoCapacity would tell the user the fleet is busy and invite a retry.
assert!(matches!(
classify_error_string("(404) No such remote session"),
RemoteSessionError::Other(_)
));
}
#[test]
fn idempotency_key_is_stable_for_one_attempt_and_distinct_across_attempts() {
let a = idempotency_key("p1", "attempt-1");
+1 -3
View File
@@ -1,7 +1,5 @@
use crate::proxy_runner::find_sidecar_executable;
#[cfg(unix)]
use crate::proxy_storage::is_process_running;
use crate::proxy_storage::{process_identity_matches, process_start_time};
use crate::proxy_storage::{is_process_running, process_identity_matches, process_start_time};
use crate::xray::{build_client_config_json, parse_vless_uri, XrayClientRuntime};
use crate::xray_worker_storage::{
create_xray_worker_log, delete_xray_worker_config, generate_xray_worker_id,