Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
323 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

preparing for cloud work1//! What the site may ask of the app: the commands behind `window.g1tDesktop`
2//! (bridge.js), and the launcher's own.
3//!
4//! Every command checks who is asking. The site's commands answer only a
5//! page of the configured server; the launcher's only the page bundled
6//! with the app. A page from anywhere else gets nothing, whatever it
7//! loads.
8
9use serde::Serialize;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r910use tauri::{AppHandle, Manager, UserAttentionType, Webview};
preparing for cloud work11use tauri_plugin_notification::NotificationExt;
12use tauri_plugin_opener::OpenerExt;
13use url::Url;
14
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r915use crate::settings::{self, Settings};
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.16use crate::{badge, config, frame, runner, updates};
preparing for cloud work17
18const SCRIPT: &str = include_str!("bridge.js");
19
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r920/// The bridge, run at the start of every page the site's web view loads:
21/// `custom` is whether g1t's title bar is above it (frame.rs).
22pub fn script(custom: bool) -> String {
23 SCRIPT
24 .replace("__FRAME__", if custom { "custom" } else { "system" })
25 .replace("__VERSION__", env!("CARGO_PKG_VERSION"))
26 .replace("__PLATFORM__", std::env::consts::OS)
27 .replace("\"__OUTSIDE__\"", &serde_json::to_string(crate::navigation::OUTSIDE).expect("a list of paths is JSON"))
preparing for cloud work28}
29
30/// What the site and the launcher learn about the app.
31#[derive(Serialize)]
32pub struct Info {
33 pub version: String,
34 pub platform: String,
35 pub server: String,
36 /// The version downloaded and waiting for a restart, if one is.
37 pub update_ready: Option<String>,
38}
39
40fn caller(webview: &Webview) -> Result<Url, String> {
41 webview.url().map_err(|error| error.to_string())
42}
43
44/// The page asking is one of the server's.
45fn from_site(app: &AppHandle, webview: &Webview) -> Result<(), String> {
46 let url = caller(webview)?;
47 if config::is_server(app, &url) { Ok(()) } else { Err("only g1t's own pages may ask that".into()) }
48}
49
50/// The page asking is the launcher.
51fn from_launcher(webview: &Webview) -> Result<(), String> {
52 let url = caller(webview)?;
53 if config::is_launcher(&url) { Ok(()) } else { Err("only the launcher may ask that".into()) }
54}
55
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r956/// The page asking is the settings window's, bundled with the app.
57fn from_settings(webview: &Webview) -> Result<(), String> {
58 from_launcher(webview)?;
59 if webview.label() == settings::SETTINGS { Ok(()) } else { Err("only the settings window may ask that".into()) }
60}
61
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.62/// The Runner window, a page bundled with the app (launcher/runner.html).
63fn from_runner(webview: &Webview) -> Result<(), String> {
64 from_launcher(webview)?;
65 if webview.label() == settings::RUNNER { Ok(()) } else { Err("only the Runner window may ask that".into()) }
66}
67
68/// The settings window, or the Runner window, which keeps the runner's settings.
69fn from_settings_or_runner(webview: &Webview) -> Result<(), String> {
70 from_settings(webview).or_else(|_| from_runner(webview))
71}
72
preparing for cloud work73fn from_either(app: &AppHandle, webview: &Webview) -> Result<(), String> {
74 from_site(app, webview).or_else(|_| from_launcher(webview))
75}
76
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r977/// The page asking is the one under the title bar, whichever it is: the
78/// site's, the launcher, or GitHub's while signing in.
79fn from_page(app: &AppHandle, webview: &Webview) -> Result<(), String> {
80 let url = caller(webview)?;
81 let shown = config::is_server(app, &url) || config::is_launcher(&url) || crate::navigation::signing_in(&url);
82 if webview.label() == frame::SITE && shown { Ok(()) } else { Err("only the page in the window may ask that".into()) }
83}
84
85/// The page asking is the title bar's, bundled with the app.
86fn from_bar(webview: &Webview) -> Result<(), String> {
87 from_launcher(webview)?;
88 if webview.label() == frame::BAR { Ok(()) } else { Err("only the title bar may ask that".into()) }
89}
90
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.91/// The page asking has window buttons: the title bar, or the settings or Runner window's own.
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r992fn from_frame(webview: &Webview) -> Result<(), String> {
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.93 from_bar(webview).or_else(|_| from_settings_or_runner(webview))
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r994}
95
preparing for cloud work96#[tauri::command]
97pub fn desktop_info(app: AppHandle, webview: Webview) -> Result<Info, String> {
98 from_either(&app, &webview)?;
99 Ok(Info {
100 version: env!("CARGO_PKG_VERSION").into(),
101 platform: std::env::consts::OS.into(),
102 server: config::server(&app).to_string(),
103 update_ready: updates::ready(&app),
104 })
105}
106
107/// A notification from the system: the site asks for one when a message
108/// is for the person and the window is behind another.
109#[tauri::command]
110pub fn notify(app: AppHandle, webview: Webview, title: String, body: String) -> Result<(), String> {
111 from_site(&app, &webview)?;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9112 let settings = config::settings(&app);
113 if let Some(window) = crate::frame::window(&app).filter(|window| settings.flash && !window.is_focused().unwrap_or(false)) {
114 let _ = window.request_user_attention(Some(UserAttentionType::Informational));
115 }
116 if !settings.notifications {
117 return Ok(());
118 }
preparing for cloud work119 app.notification().builder().title(title).body(body).show().map_err(|error| error.to_string())
120}
121
122/// What needs the person, on the icon and the tray; 0 clears it.
123#[tauri::command]
124pub fn set_badge(app: AppHandle, webview: Webview, count: i64) -> Result<(), String> {
125 from_site(&app, &webview)?;
126 badge::set(&app, count.max(0));
127 Ok(())
128}
129
130/// A link to somewhere else, opened in the browser the system uses.
131#[tauri::command]
132pub fn open_url(app: AppHandle, webview: Webview, url: String) -> Result<(), String> {
133 from_either(&app, &webview)?;
134 let parsed = Url::parse(&url).map_err(|error| error.to_string())?;
135 if !matches!(parsed.scheme(), "http" | "https" | "mailto") {
136 return Err(format!("{} links stay in the app", parsed.scheme()));
137 }
138 app.opener().open_url(parsed.as_str(), None::<&str>).map_err(|error| error.to_string())
139}
140
141/// Whether a server answers at all: the launcher asks before the window
142/// goes there, so a machine with no network sees a page of ours, not the
143/// web view's own error.
144#[tauri::command]
145pub async fn reachable(app: AppHandle, webview: Webview, url: Option<String>) -> Result<bool, String> {
146 from_launcher(&webview)?;
147 let target = match url {
148 Some(text) => config::parse(&text).ok_or("That is not a web address.")?,
149 None => config::server(&app),
150 };
151 let client = reqwest::Client::builder()
152 .timeout(std::time::Duration::from_secs(6))
153 .build()
154 .map_err(|error| error.to_string())?;
155 Ok(client.head(target).send().await.is_ok())
156}
157
158/// The launcher chose a server: it is kept, and the window goes there next.
159#[tauri::command]
160pub fn set_server(app: AppHandle, webview: Webview, url: String) -> Result<String, String> {
161 from_launcher(&webview)?;
162 config::set_server(&app, &url).map(|server| server.to_string())
163}
164
165/// Looks for an update now: `current`, `ready:<version>`, or an error.
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9166/// The settings window shows the answer itself; anyone else asking is also
167/// told with a notification.
preparing for cloud work168#[tauri::command]
169pub async fn check_for_updates(app: AppHandle, webview: Webview) -> Result<String, String> {
170 from_either(&app, &webview)?;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9171 updates::check(&app, webview.label() != settings::SETTINGS).await
172}
173
174/// A title bar button: `minimize`, `maximize` (which toggles), `close`,
175/// `drag`, or `maximized` to ask. Answers whether the window is maximized.
176#[tauri::command]
177pub fn window_control(app: AppHandle, webview: Webview, action: String) -> Result<bool, String> {
178 from_frame(&webview)?;
179 crate::frame::control(&app, &webview.window(), &action)
180}
181
182/// The title bar's menu button: the app's menu, at that place in the window.
183#[tauri::command]
184pub fn window_menu(webview: Webview, x: f64, y: f64) -> Result<(), String> {
185 from_bar(&webview)?;
186 crate::frame::menu(&webview.window(), x, y)
187}
188
189/// The page under the title bar says what it is: its title, its search,
190/// whether there is a way back, its colours (frame.rs).
191#[tauri::command]
192pub fn title_bar_page(app: AppHandle, webview: Webview, page: frame::Page) -> Result<(), String> {
193 from_page(&app, &webview)?;
194 frame::page(&app, page);
195 Ok(())
196}
197
198/// What the title bar shows now: it asks as it loads.
199#[tauri::command]
200pub fn title_bar_current(app: AppHandle, webview: Webview) -> Result<frame::Page, String> {
201 from_bar(&webview)?;
202 Ok(frame::current(&app))
203}
204
205/// A title bar button that acts on the page: `back`, `forward`, `search`, `focus`.
206#[tauri::command]
207pub fn title_bar_act(app: AppHandle, webview: Webview, action: String) -> Result<(), String> {
208 from_bar(&webview)?;
209 frame::act(&app, &action)
210}
211
212/// Restarts g1t, installing a downloaded update on the way: the settings
213/// window offers it after a change that needs one.
214#[tauri::command]
215pub fn restart(app: AppHandle, webview: Webview) -> Result<(), String> {
216 from_settings(&webview)?;
217 if updates::ready(&app).is_some() {
218 updates::restart(&app);
219 }
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.220 runner::shutdown(&app);
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9221 app.restart()
222}
223
224/// The settings window's "Restart now": installs the downloaded update and starts it.
225#[tauri::command]
226pub fn restart_to_update(app: AppHandle, webview: Webview) -> Result<(), String> {
227 from_settings(&webview)?;
228 updates::restart(&app);
229 Ok(())
230}
231
232/// Opens the app's settings window: the site's account menu asks for it.
233#[tauri::command]
234pub fn open_settings(app: AppHandle, webview: Webview) -> Result<(), String> {
235 from_either(&app, &webview)?;
236 settings::open(&app);
237 Ok(())
238}
239
240/// What the settings window shows.
241#[tauri::command]
242pub fn get_settings(app: AppHandle, webview: Webview) -> Result<settings::View, String> {
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.243 from_settings_or_runner(&webview)?;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9244 Ok(settings::view(&app))
245}
246
247/// The settings window changed something: everything but opening at sign-in.
248#[tauri::command]
249pub fn save_settings(app: AppHandle, webview: Webview, settings: Settings) -> Result<settings::View, String> {
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.250 from_settings_or_runner(&webview)?;
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9251 settings::apply(&app, settings)?;
252 Ok(settings::view(&app))
253}
254
255/// Opening at sign-in, which the system keeps.
256#[tauri::command]
257pub fn set_open_at_login(app: AppHandle, webview: Webview, on: bool) -> Result<settings::View, String> {
258 from_settings(&webview)?;
259 settings::open_at_login(&app, on)?;
260 Ok(settings::view(&app))
261}
262
263/// The settings window's "Change…": the main window goes to the launcher's choice of server.
264#[tauri::command]
265pub fn change_server(app: AppHandle, webview: Webview) -> Result<(), String> {
266 from_settings(&webview)?;
267 if let Some(window) = app.get_webview_window(settings::SETTINGS) {
268 let _ = window.close();
269 }
270 crate::tray::launcher(&app, "change");
271 Ok(())
272}
273
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.274/// The runner's state, diagnosis and recent log: for the site's card for
275/// this computer and the settings window.
276#[tauri::command]
277pub fn runner_status(app: AppHandle, webview: Webview) -> Result<runner::View, String> {
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.278 from_site(&app, &webview).or_else(|_| from_settings_or_runner(&webview)).or_else(|_| from_bar(&webview))?;
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.279 Ok(runner::view(&app))
280}
281
282/// The site, on every page: whether someone is signed in, the workspaces
283/// they own (the only ones they can add runners to), and the one they are
284/// in. Answers the workspace a registration token is wanted for, if one is.
285#[tauri::command]
286pub fn runner_offer(app: AppHandle, webview: Webview, signed_in: bool, workspaces: Vec<runner::Offered>, current: Option<String>) -> Result<Option<String>, String> {
287 from_site(&app, &webview)?;
288 runner::offer(&app, signed_in, workspaces, current);
289 Ok(runner::wanted(&app))
290}
291
292/// The site made a registration token for the workspace the app asked
293/// for, or says why not: `allowed` is false when the person may not add
294/// runners there. The token goes to the runner and nowhere else.
295#[tauri::command]
296pub fn runner_register(app: AppHandle, webview: Webview, workspace: String, token: Option<String>, allowed: bool, error: Option<String>) -> Result<(), String> {
297 from_site(&app, &webview)?;
298 runner::register(&app, workspace, token, allowed, error);
299 Ok(())
300}
301
302/// A button for the runner: Start, Stop, or a diagnosis's fix
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.303/// (runner/diagnosis.rs), from settings or the site's card. With a kind
304/// of job (`capability`: `linux`, `windows`), Start and Stop are its alone.
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.305#[tauri::command]
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.306pub fn runner_control(app: AppHandle, webview: Webview, action: String, capability: Option<String>) -> Result<(), String> {
The desktop app is 0.3.4, and the runner has a window of its own. Beside the title bar's Settings button a chip says what the runner is doing, live, with a dot in its colour (Runner running, Running a job, Running 2 jobs, Runner starting, Runner updating, Runner stopped, Runner offline, Runner needs you), as Docker Desktop shows its engine; in a narrow window only the dot shows. It opens the Runner window, as the tray's runner row now does: at its top what the whole runner is doing, with what it is running, its version and workspace, and its own Stop or Start; then each kind of job's card as before; then its setup (running jobs here at all, how jobs run, the workspace, the computer with Check for runner updates and View on g1t); and its log, open and following along as it is written. Settings keeps the runner as one line, what it is doing with Open runner, and each window saves only its own settings over what the app has now, so neither undoes the other's. The chip's words are diagnosis.rs's, the Runner window is made off the main thread as Settings now is, and it has a capability of its own with only the commands it uses; the title bar may now ask the runner's status. Two things fixed on the way: a settings row hidden until it applies, Restart to change the title bar, showed all the time, since its row's display beat the hidden attribute; and in a narrow window the title bar's buttons ran over the search, which now shrinks instead. The desktop guide describes the chip, the Runner window and where each runner setting now is.307 from_site(&app, &webview).or_else(|_| from_settings_or_runner(&webview))?;
Merge the desktop runner's settings: a card for each kind of job, Linux and Windows, each with its state, job, cause and fix, and its own Start and Stop; an honest wait for a release's sandbox images, checked again by itself; and the same on the Runners page.308 runner::act(&app, &action, capability.as_deref())
Merge the desktop app's runner: it starts with the app for the signed-in person, sandboxed by default and running nothing where no sandbox is available yet, registers through the site, keeps itself up to date, and says everywhere what it is doing, why it is not, and how to fix it.309}
310
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9311#[cfg(test)]
312mod tests {
313 #[test]
314 fn the_bridge_has_nothing_left_to_fill_in() {
315 let script = super::script(true);
316 for placeholder in ["__VERSION__", "__PLATFORM__", "__OUTSIDE__", "__FRAME__"] {
317 assert!(!script.contains(placeholder), "{placeholder} is left in the bridge");
318 }
319 assert!(script.contains(r#"var FRAME = "custom";"#));
320 assert!(super::script(false).contains(r#"var FRAME = "system";"#));
321 assert!(script.contains(r#"var OUTSIDE = ["/pricing""#));
322 }
preparing for cloud work323}

This file's history is long; its oldest lines are credited to the oldest commit read.