Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1eeaad925 | ||
|
|
9952170aae | ||
|
|
19bb6767ff | ||
|
|
7997be6373 | ||
|
|
cbbfe43b94 | ||
|
|
0a53017319 | ||
|
|
eac5db5dad | ||
|
|
9424ba9cc3 | ||
|
|
188a458930 | ||
|
|
e2d747d7d3 | ||
|
|
660058f0c0 | ||
|
|
5b1015d006 | ||
|
|
5384c83a0f | ||
|
|
4f2c0def94 | ||
|
|
caf0a830a9 |
@@ -13,8 +13,8 @@ These are requirements, not preferences. Changing one needs the owner's agreemen
|
||||
A deploy is a release that isn't uploaded: the same notarized disk image, installed on the owner's Macs instead of published. When asked to deploy, do exactly this, from the worktree root:
|
||||
|
||||
1. Run `mac/deploy.sh`, always as the last action of the turn. It runs `mac/package.sh` (the build, both notarizations and the disk image, as for a release, into `mac/.build/release/<version>/`), builds the iPhone app with `mac/phone.sh` and uploads it to TestFlight (`docs/iphone.md`), and installs the image's app as `/Applications/Byre.app` with `mac/install.sh`. The service runs `/Applications/Byre.app/Contents/Helpers/byre`; within 5 seconds it sees that file replaced and exits, and launchd starts the new server and `byre-hands`. Everything is signed with Developer ID, so the macOS privacy grants (Screen Recording, Accessibility, Automation) survive.
|
||||
2. When you resume, confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/tailnet` at the loopback address (`byre check` prints it as `loopback`), whose `state` must be `Running`. Report what you deployed and what you checked.
|
||||
3. Once the service is confirmed healthy, update each client Mac whose app is running, through the client tools and without asking first: upload the disk image (`mac/.build/release/<version>/byre-<version>.dmg`) and `mac/install.sh`, quit the app, run `install.sh <dmg>`, and `open /Applications/Byre.app`. The app reads its server address from its iCloud-synced SwiftData store, so it reconnects to prod with no setup. Report each Mac's app commit (`install.sh` prints it).
|
||||
2. When you resume, confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/tailnet` at the loopback address (`byre check` prints it as `loopback`), whose `state` must be `Running`. Report the new build number first (`plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist` prints it; the iPhone build uploaded to TestFlight has the same one), then what you deployed and what you checked.
|
||||
3. Once the service is confirmed healthy, update each client Mac whose app is running, through the client tools and without asking first: upload the disk image (`mac/.build/release/<version>/byre-<version>.dmg`) and `mac/install.sh`, quit the app, run `install.sh <dmg>`, and `open /Applications/Byre.app`. The app reads its server address from its iCloud-synced SwiftData store, so it reconnects to prod with no setup. Report each Mac's build number (`install.sh` prints it, then the commit).
|
||||
4. End the report with release notes for everything on main since the last release tag (`git describe --tags --abbrev=0` names it), written for a byre user in the "Releasing" shape (`## New` and `## Fixed`, each a bullet with a bold short name and what it does or what now works; no `## Install`). Read `git diff <tag>..HEAD`, not just the commit subjects, and leave out changes no user sees.
|
||||
|
||||
Deploying means running `mac/deploy.sh` on iso. Never stand in for it by running `mac/package.sh` and `mac/install.sh` yourself, not even to build once for several Macs. Every other Mac gets the disk image `mac/deploy.sh` built, after iso is confirmed healthy (step 3).
|
||||
@@ -52,7 +52,7 @@ When the owner tells the lead "deploy byre", or anything that means the same, th
|
||||
|
||||
- Exactly two panes, side by side, each exactly half the width, at every window size. Never three or more columns, never a sidebar, never a resizable or collapsible split.
|
||||
- No navbar. The panes fill the window, below only the identity banner when it shows.
|
||||
- The left pane has six tabs: Lead (the user's lead agent), Notes (what the lead has noted about the user), Workflows (the saved workflows), Status, Server (the byre service on this Mac) and Settings (the user's and this client's preferences), with the connection dot and the user's name at the end of its tab strip. Nothing else of the user's goes in it. The right pane always shows one task's conversation (its transcript, or a workflow stage task's run), or a project's dashboard when no task is selected; the lead sets the task, and the human can switch it (⌘K). The task's other views (its other tabs, and images and diagrams zoomed from its transcript) open only when the human opens them, in a sheet over the left pane: attached to the center line, in the task's color and named by it, leaving a strip of the left pane visible that closes it. Images and diagrams zoomed from the lead chat open the same way over the right pane. A new task view is a new tab that opens in that sheet, never a new column or region.
|
||||
- The left pane has six tabs: Lead (the user's lead agent), Status, Notes (what the lead has noted about the user), Workflows (the saved workflows), Server (the byre service on this Mac) and Settings (the user's and this client's preferences), with the connection dot and the user's name at the end of its tab strip. Nothing else of the user's goes in it. The right pane always shows one task's conversation (its transcript, or a workflow stage task's run), or a project's dashboard when no task is selected; the lead sets the task, and the human can switch it (⌘K). The task's other views (its other tabs, and images and diagrams zoomed from its transcript) open only when the human opens them, in a sheet over the left pane: attached to the center line, in the task's color and named by it, leaving a strip of the left pane visible that closes it. Images and diagrams zoomed from the lead chat open the same way over the right pane. A new task view is a new tab that opens in that sheet, never a new column or region.
|
||||
- Overlays (the task switcher, confirmation dialogs) are allowed; they sit above the panes and don't change the layout.
|
||||
|
||||
## UI look
|
||||
|
||||
+4
-2
@@ -2312,12 +2312,14 @@ components:
|
||||
|
||||
ConnectedClient:
|
||||
type: object
|
||||
required: [id, login, host, commit, connected_at]
|
||||
required: [id, login, host, device, commit, build, connected_at]
|
||||
properties:
|
||||
id: { type: string, description: The ID the app made up when it started, from its Byre-Client header. }
|
||||
login: { type: string }
|
||||
host: { type: string, description: The Mac's name from its Byre-Host header. }
|
||||
host: { type: string, description: The device's name from its Byre-Host header. }
|
||||
device: { type: string, description: "The kind of device the app runs on (Mac or iPhone), from its Byre-Device header." }
|
||||
commit: { type: string, description: The git commit the app was built from, from its Byre-Commit header; empty when the client sent none. }
|
||||
build: { type: string, description: "The app's build number (its CFBundleVersion, the Unix time of that commit), from its Byre-Build header; empty when the client sent none." }
|
||||
connected_at: { type: string, format: date-time, description: When the client's WebSocket connected. }
|
||||
last_action_at: { type: string, format: date-time, description: When byre last received a request the human made from this client since byre started; absent when none. }
|
||||
sync: { $ref: "#/components/schemas/ClientSync" }
|
||||
|
||||
+7
-3
@@ -1,15 +1,19 @@
|
||||
# byre for iPhone
|
||||
|
||||
The iPhone app is the same `Byre` target as the Mac app, built for iOS: platform differences are `#if os(macOS)` / `#if os(iOS)` blocks, with the small shared aliases (`PlatformImage`, `PlatformColor`, `PlatformFont`, `PlatformView`, `touchOnly`, `deviceKind`) in `mac/Sources/Byre/Platform.swift`. Only the layout is the phone's own (`Phone.swift`): a tab bar with Lead, Tasks and More. Tasks lists every project's active tasks with the Mac's own task rows; a task opens pushed, with the Mac's task bar and tab strip on top and the tab's view below in place of the Mac's attached sheet. Zoomed images and diagrams cover the screen. More holds Notes, Workflows, Status and Settings. Every action that focuses a task or project (a row, a link, a notification, the switcher) bumps `Store.reveals`, which brings the Tasks tab forward with the task open; the server's own focus changes don't.
|
||||
The iPhone app is the same `Byre` target as the Mac app, built for iOS: platform differences are `#if os(macOS)` / `#if os(iOS)` blocks, with the small shared aliases (`PlatformImage`, `PlatformColor`, `PlatformFont`, `PlatformView`, `touchOnly`, `deviceKind`) in `mac/Sources/Byre/Platform.swift`. Only the layout is the phone's own (`Phone.swift`): a tab bar with Lead, Tasks and More. Tasks lists every project's active tasks with the Mac's own task rows; a task opens pushed, with the Mac's task bar and tab strip on top and the tab's view below in place of the Mac's attached sheet. Zoomed images and diagrams cover the screen. More holds Status (shown first), Notes, Workflows and Settings. Every action that focuses a task or project (a row, a link, a notification, the switcher) bumps `Store.reveals`, which brings the Tasks tab forward with the task open; the server's own focus changes don't.
|
||||
|
||||
Left out on the phone: the Server tab and running byre locally, the client connection (`Remote`: the phone is never a client for agents' client tools, so `list_clients` doesn't show it), the Sparkle updater, and keyboard shortcuts. Notifications come from the app's own event stream, so they only show while the app is open; push notifications would need APNs on the server.
|
||||
The phone holds the client connection (`Remote`) like a Mac, so agents can show it pages and files and drive its Page view (`show_page`, `show_file`, `drive_page`); it answers every other client request with an error. It sends `Byre-Device: iPhone` (Macs send `Mac`), and the server signs commits and runs `byre client-exec` without `--client` only on Macs.
|
||||
|
||||
Left out on the phone: the Server tab and running byre locally, the shell, file, hands and signing client requests, the Sparkle updater, and keyboard shortcuts. Notifications come from the app's own event stream, so they only show while the app is open; push notifications would need APNs on the server.
|
||||
|
||||
## Building
|
||||
|
||||
`mac/phone.sh` builds the app with `xcodebuild` (a Swift package can't be built for iOS by `swift build`) in the same shared directory and under the same lock as `mac/bundle.sh`, assembles `Byre.app` itself (icon through `actool`, fonts, `Info.plist` from `mac/phone/Info.plist` plus the version, commit and `DT*` keys App Store Connect checks), signs it ad hoc with `mac/phone/byre.entitlements`, and wraps it in `mac/.build/phone/iphoneos/Byre.xcarchive`. Release only, like the Mac app. `xcodebuild -exportArchive -archivePath <archive> -exportOptionsPlist mac/phone/export.plist -exportPath <dir> -allowProvisioningUpdates` re-signs it for the App Store and uploads it to TestFlight, using the Apple account Xcode is signed into: Xcode makes the distribution certificate and profile itself, so none are kept in the repo. Uploading needs the App ID `run.fc.byre.ios` (iCloud with the container `iCloud.run.fc.byre.mac`, and Push Notifications) and its app record in App Store Connect; neither can be made through Apple's API. The version is the last release tag's (App Store Connect takes only numbers) and the build number the UTC build time, `YYYYMMDD.HHMMSS`: App Store Connect refuses a build number that isn't higher than every earlier upload, and a commit count can go down when a branch with many commits uploads before its squash merge lands on main.
|
||||
`mac/phone.sh` builds the app with `xcodebuild` (a Swift package can't be built for iOS by `swift build`) in the same shared directory and under the same lock as `mac/bundle.sh`, assembles `Byre.app` itself (icon through `actool`, fonts, `Info.plist` from `mac/phone/Info.plist` plus the version, commit and `DT*` keys App Store Connect checks), signs it ad hoc with `mac/phone/byre.entitlements`, and wraps it in `mac/.build/phone/iphoneos/Byre.xcarchive`. Release only, like the Mac app. `xcodebuild -exportArchive -archivePath <archive> -exportOptionsPlist mac/phone/export.plist -exportPath <dir> -allowProvisioningUpdates` re-signs it for the App Store and uploads it to TestFlight, using the Apple account Xcode is signed into: Xcode makes the distribution certificate and profile itself, so none are kept in the repo. Uploading needs the App ID `run.fc.byre.ios` (iCloud with the container `iCloud.run.fc.byre.mac`, and Push Notifications) and its app record in App Store Connect; neither can be made through Apple's API. The version is the last release tag's (App Store Connect takes only numbers) and the build number is the Mac app's (`docs/operations.md`, "Releases"). App Store Connect refuses a build number that isn't higher than every earlier upload, so a second upload of the same commit fails; a commit count wouldn't do, since it can go down when a branch with many commits uploads before its squash merge lands on main.
|
||||
|
||||
`mac/phone.sh --simulator` builds an ad-hoc-signed app for the iOS simulator instead, at `mac/.build/phone/iphonesimulator/Byre.app`; install it with `xcrun simctl install <device> <app>` and start it with `xcrun simctl launch <device> run.fc.byre.ios`. The simulator ignores entitlements given to `codesign` and refuses to launch an ad-hoc app signed with restricted ones, so the script links `mac/phone/byre.entitlements` into the binary's `__TEXT,__entitlements` section, as Xcode does; without the iCloud entitlement CloudKit raises at launch. Its node signs in to Tailscale like any other: `xcrun simctl launch --console-pty` prints libtailscale's sign-in URL, which changes on every launch.
|
||||
|
||||
The simulator's window (Xcode's Device Hub) lists the app's own accessibility elements, so hands' `ax_tree` and `act` `press` drive the app without a mouse. Hands' mouse steps (`click`, `drag`, `scroll`) refuse that window, because Device Hub reports no process id; a gesture such as dragging the keyboard away needs mouse events posted with `CGEvent` instead. `xcrun simctl io booted screenshot --display LCD <file>` captures the app at full resolution.
|
||||
|
||||
## Platform facts
|
||||
|
||||
- CloudKit on iOS requires the `remote-notification` background mode in `Info.plist`, even though the app sets up no push of its own.
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ Each preview gets its own port on byre's node rather than a path under byre's or
|
||||
|
||||
## Releases
|
||||
|
||||
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app` with the server, `byre-hands` and `byre-update` in `Contents/Helpers`. Images are served from `https://dl.byre.run/`; each release's notes are on `https://g.fc.run/ai/byre/releases`. `mac/package.sh` builds one, `mac/deploy.sh` installs it locally (see `AGENTS.md`, "Deploying"), and `mac/release.sh <notes>` publishes it from a clean checkout at a pushed `v<version>` tag. A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it.
|
||||
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app` with the server, `byre-hands` and `byre-update` in `Contents/Helpers`. Images are served from `https://dl.byre.run/`; each release's notes are on `https://g.fc.run/ai/byre/releases`. `mac/package.sh` builds one, `mac/deploy.sh` installs it locally (see `AGENTS.md`, "Deploying"), and `mac/release.sh <notes>` publishes it from a clean checkout at a pushed `v<version>` tag. The build number (`CFBundleVersion`) of both the Mac and iPhone apps is the Unix time of the commit they were built from (`git log -1 --format=%ct`), so one deploy's apps share it; the apps send it in their `Byre-Build` header and the Status tab's client list shows it. A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it.
|
||||
|
||||
The appcast must never name an image that isn't available yet, and a published g.fc.run release must already have its attachments. If a failed release run leaves a draft on the releases page, delete it there before running again.
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ func (m *Manager) clientTools(l *live) []mcp.Tool {
|
||||
Description: "List the Macs with byre's app open: the human's own machines, not the one byre (and your worktree) runs on. " +
|
||||
"Each has its name, which the other client tools take as client, the id of that running app (pass it as client instead when two apps are open on one Mac), the human using it, and when the human last did something in byre from it. " +
|
||||
"A message from the human says which Mac it was sent from (\"Sent from <name> (client <id>)\"); that is the Mac the human sits at, so show things there, naming it by that id. " +
|
||||
"The most recently active one is listed first.",
|
||||
"The most recently active one is listed first. " +
|
||||
"The human's iPhone is listed too, when byre's app is open on it; it takes only show_page, show_file and drive_page, and the other client tools fail there.",
|
||||
InputSchema: json.RawMessage(`{"type": "object", "properties": {}}`),
|
||||
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
|
||||
return m.listClients(), nil
|
||||
@@ -289,13 +290,13 @@ func (m *Manager) listClients() string {
|
||||
now := time.Now()
|
||||
b := &strings.Builder{}
|
||||
for i, r := range rows {
|
||||
fmt.Fprintf(b, "- %s (%s, id %s): ", r.Host, r.Login, r.ID)
|
||||
fmt.Fprintf(b, "- %s (%s, %s, id %s): ", r.Host, r.Device, r.Login, r.ID)
|
||||
if r.LastActionAt == nil {
|
||||
b.WriteString("no action since byre started")
|
||||
} else {
|
||||
fmt.Fprintf(b, "last action %s ago", now.Sub(*r.LastActionAt).Round(time.Second))
|
||||
}
|
||||
fmt.Fprintf(b, ", connected %s ago, app built from %s", now.Sub(r.ConnectedAt).Round(time.Second), r.Commit)
|
||||
fmt.Fprintf(b, ", connected %s ago, app build %s from %s", now.Sub(r.ConnectedAt).Round(time.Second), r.Build, r.Commit)
|
||||
if r.Sync != nil && !r.Sync.Ok {
|
||||
fmt.Fprintf(b, ", iCloud sync failing since %s ago: %s", now.Sub(r.Sync.At).Round(time.Second), *r.Sync.Error)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ byre client-exec [--client NAME] [--timeout SECS] -- CMD...
|
||||
`byre` is `/Applications/Byre.app/Contents/Helpers/byre` on every Mac with the app.
|
||||
|
||||
- It runs CMD as `client_bash` does: zsh as a login shell in the home directory, in the app's session, as the human. That session is what GUI-only commands need.
|
||||
- Without `--client` it picks the most recently active client, the one `list_clients` lists first.
|
||||
- Without `--client` it picks the most recently active Mac, skipping the iPhone, which runs no commands.
|
||||
- Each argument reaches the client as one word, so shell syntax needs `-- zsh -c '...'`.
|
||||
- The command's stdout and stderr come back separately and byte for byte, once it ends, written to client-exec's own stdout and stderr.
|
||||
- Exit status: the command's own; 75 when no client is connected, it didn't acknowledge the request within 2s, or it disconnected; 124 when `--timeout` (default 30s) killed it. A one-line `byre client-exec: ...` on stderr says why.
|
||||
|
||||
+4
-32
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
const leadGuidance = `You are the human's lead agent in byre, shown in the left pane of byre's app. The human talks to you there, and you coordinate every task agent on their behalf. You don't do task work yourself: task agents work in their own worktrees, and you read, direct and report on them.
|
||||
- Signals: byre sends you, as messages from byre, what happens across tasks: questions that task agents ask, errors and rate limits their sessions hit, turns they finish with their final message, a task's agent saying the task is ready to merge (with its overview of the whole task, which byre asks for once the review of its latest work has finished and its turn has ended) or suggesting the human discard it (with its reasoning), questions the human answered, tasks created, reviews started and finished, a merge held behind another task's or set to start after the review (and that canceled or dropped), starting to merge, merged or discarded, and every change of a task's state, as "<task> state: <before> → <now>." listed first among what happened at the same moment. State changes, reviews starting, and tasks the human created, started merging, merged or discarded never wake you on their own; they arrive only alongside another signal. When something waits on a task closing (the human says "after that merge, deploy", or you merged it for them and have more to do), call wake_on with the task right away: its next merged or discarded signal then wakes you, once. The states are running, idle, finished (a workflow stage's task once its finish_stage has taken), review_pending, reviewing, merging, discarding, merged, discarded and no_agent. A finished turn arrives with its state change: review_pending when byre is about to review the turn's work, reviewing while the review runs; neither is done, since the review can still find problems, so don't treat a task's work as finished until its state goes to idle or finished. Several may arrive in one message, followed by the current task list (as list_tasks gives it) and what your right pane shows, so you rarely need to call list_tasks after a signal. The human doesn't see signals. Messages from the human arrive separately.
|
||||
- Signals: byre sends you, as messages from byre, what happens across tasks: questions that task agents ask, errors and rate limits their sessions hit, turns they finish with their final message, a task's agent saying the task is ready to merge (with its overview of the whole task, which byre asks for once the review of its latest work has finished and its turn has ended) or suggesting the human discard it (with its reasoning), questions the human answered, tasks created, reviews started and finished, a merge held behind another task's or set to start after the review (and that canceled or dropped), starting to merge, merged or discarded, and every change of a task's state, as "<task> state: <before> → <now>." listed first among what happened at the same moment. State changes, reviews starting, and tasks the human created or started merging never wake you on their own; they arrive only alongside another signal. A task merged or discarded always wakes you, whoever closed it. The states are running, idle, finished (a workflow stage's task once its finish_stage has taken), review_pending, reviewing, merging, discarding, merged, discarded and no_agent. A finished turn arrives with its state change: review_pending when byre is about to review the turn's work, reviewing while the review runs; neither is done, since the review can still find problems, so don't treat a task's work as finished until its state goes to idle or finished. Several may arrive in one message, followed by the current task list (as list_tasks gives it) and what your right pane shows, so you rarely need to call list_tasks after a signal. The human doesn't see signals. Messages from the human arrive separately.
|
||||
- Don't respond to what the human just did. A signal about their own action (a question they answered, a task they created, merged, started merging or discarded) tells them nothing new: don't report, confirm or comment on it. Act on it only if it changes what you are doing, and then end the turn with one short line at most. When a batch of signals needs nothing from you to the human, make your final message exactly NO_REPLY and nothing else; byre folds that turn away so the human doesn't see it.
|
||||
- "Your right pane is free" means the human finished what was in the right pane. Show them the next thing that needs them, most urgent first (questions, then errors in the task's transcript, then finished turns, then tasks ready to merge or suggested for discarding), with one line saying what it is. If nothing needs them, show that project with no task, then post a short status: one line per active task (what it's doing or waiting on), then up to three TODO items you'd start next (from list_tasks), each with a one-line reason. Write each item as a link to #/todo/<project_id>/<title, URL-encoded with parentheses as %28 and %29>, for example [Fix login redirect](#/todo/prj3f2a9C1b7dQ/Fix%20login%20redirect): byre draws it with a Start button the human can click, and once the task exists, its state. Start one with start_todo only when the human picks it. If that says nothing new since your last status, make your final message NO_REPLY.
|
||||
- Whenever you suggest new work that isn't in TODO.md (a task you think is worth doing, an improvement to byre or to your own role), write it as a link to #/suggest/<project_id>/<title>/<brief>, both URL-encoded with parentheses as %28 and %29, for example [Lead tool for admin status](#/suggest/prj3f2a9C1b7dQ/Lead%20tool%20for%20admin%20status/Add%20a%20lead%20tool%20that%20reads%20%2Fapi%2Fadmin%2Fstatus%20...). The brief is the new task agent's whole first message, so make it a complete brief, as for create_task. byre draws the link with two buttons: Start creates the task with that title and brief at once, without you, and Discuss sends you a message asking to talk the suggestion over. Once an active task has that title, it shows the task's state instead. Don't ask whether to start a suggestion; the buttons do that.
|
||||
@@ -427,10 +427,6 @@ func (m *Manager) signal(ctx context.Context, batch []api.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
woken, err := m.takeWakes(ctx, batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
batch, err = m.dropBackground(ctx, batch)
|
||||
if err != nil || len(batch) == 0 {
|
||||
return err
|
||||
@@ -450,8 +446,7 @@ func (m *Manager) signal(ctx context.Context, batch []api.Event) error {
|
||||
lines := []string{}
|
||||
loud := false
|
||||
for i, ev := range batch {
|
||||
wake := closeSignal(ev) && woken[deref(ev.TaskId)][owner]
|
||||
if ev.Actor == leadActor(owner) && !wake {
|
||||
if ev.Actor == leadActor(owner) && !closeSignal(ev) {
|
||||
continue
|
||||
}
|
||||
freed, err := m.freedText(ctx, owner, ev)
|
||||
@@ -462,10 +457,7 @@ func (m *Manager) signal(ctx context.Context, batch []api.Event) error {
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if wake {
|
||||
text += "\nYou asked to be woken by this with wake_on."
|
||||
}
|
||||
if wake || freed != "" || !quietSignal(ev) {
|
||||
if freed != "" || !quietSignal(ev) {
|
||||
loud = true
|
||||
}
|
||||
if ev.Kind == "task.state" {
|
||||
@@ -490,26 +482,6 @@ func closeSignal(ev api.Event) bool {
|
||||
return (ev.Kind == "task.merged" || ev.Kind == "task.discarded") && ev.TaskId != nil
|
||||
}
|
||||
|
||||
func (m *Manager) takeWakes(ctx context.Context, batch []api.Event) (map[string]map[string]bool, error) {
|
||||
woken := map[string]map[string]bool{}
|
||||
for _, ev := range batch {
|
||||
if !closeSignal(ev) {
|
||||
continue
|
||||
}
|
||||
owners, err := m.store.TakeLeadWakes(ctx, *ev.TaskId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, owner := range owners {
|
||||
if woken[*ev.TaskId] == nil {
|
||||
woken[*ev.TaskId] = map[string]bool{}
|
||||
}
|
||||
woken[*ev.TaskId][owner] = true
|
||||
}
|
||||
}
|
||||
return woken, nil
|
||||
}
|
||||
|
||||
func (m *Manager) dropBackground(ctx context.Context, batch []api.Event) ([]api.Event, error) {
|
||||
kept := []api.Event{}
|
||||
for _, ev := range batch {
|
||||
@@ -531,7 +503,7 @@ func quietSignal(ev api.Event) bool {
|
||||
switch ev.Kind {
|
||||
case "task.state", "review.started":
|
||||
return true
|
||||
case "task.created", "task.merge_started", "task.merged", "task.discarded":
|
||||
case "task.created", "task.merge_started":
|
||||
return humanActor(ev.Actor)
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -408,7 +408,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
||||
"Merge a task as the human: its agent merges the default branch in, then byre squashes it into one commit, pushes it to origin and closes the task. "+
|
||||
"While another task in the project is merging, byre holds the merge and starts it when that one is done. "+
|
||||
"For a subtask, target parent instead asks its parent task's agent to merge it into the parent's branch, and the subtask closes once the parent has. "+
|
||||
"Refused for a task with active subtasks. Only when the human asks. Its merged signal arrives only alongside another signal unless you call wake_on for the task; failures show as errors in the task's transcript, and you get a signal for each."+stoppedRefusal,
|
||||
"Refused for a task with active subtasks. Only when the human asks. Its merged signal wakes you; failures show as errors in the task's transcript, and you get a signal for each."+stoppedRefusal,
|
||||
`{"type": "object", "properties": {
|
||||
"task_id": {"type": "string"},
|
||||
"target": {"type": "string", "enum": ["main", "parent"], "description": "Default main."}
|
||||
@@ -489,29 +489,6 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
||||
return fmt.Sprintf("Discarding %s: its agent gets a turn to write the task summary, and byre discards the task when that turn ends.", task.Id), nil
|
||||
}),
|
||||
|
||||
tool("wake_on",
|
||||
"Make an active task's next merged or discarded signal wake you, once, whoever merged or discarded it. "+
|
||||
"Without it, a merge or discard by the human only arrives alongside another signal. Use it when something waits on that task closing, "+
|
||||
"for example \"after that merge, deploy\".",
|
||||
taskOnly,
|
||||
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
||||
task, _, err := m.taskArg(ctx, raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if task.Background {
|
||||
return "", fmt.Errorf("%w: %s is a background task, whose signals never reach you", ErrInvalid, task.Id)
|
||||
}
|
||||
err = m.store.AddLeadWake(ctx, user.Login, task.Id)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return "", fmt.Errorf("%w: %s has already been merged or discarded", ErrInvalid, task.Id)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("You'll be woken when %s is merged or discarded.", task.Id), nil
|
||||
}),
|
||||
|
||||
tool("show",
|
||||
"Point the human's right pane at a task, which shows its transcript (a workflow stage task's run: its graph and each stage's state, attempts and outputs), or at a project with no task, which shows the project's dashboard: its active tasks and its TODO.md items. "+
|
||||
"The task's other views (changes, files, terminal, board, project) open over the human's left pane only when the human opens them; you can't open them. "+
|
||||
|
||||
@@ -14,7 +14,9 @@ import (
|
||||
)
|
||||
|
||||
const readyPromptAsk = "If it isn't ready (work remains that you mean to do, or you're waiting on the human), call neither: say what's left in one line and end your turn. " +
|
||||
"If you're waiting on the human for a decision or input, ask it with ask_human now, even if an earlier message already put it in prose, so it reaches them; only an open ask_human question already counts as asked. Don't change any files this turn."
|
||||
"If you're waiting on the human for a decision or input, ask it with ask_human now, even if an earlier message already put it in prose, so it reaches them. " +
|
||||
"If this task is only a discussion with the human (questions, explanations, advice, no work to land) and the discussion is still going, ignore this message: call none of these and end your turn with one line, so the discussion can continue. " +
|
||||
"If you already called ready_for_merge, suggest_discard or ask_human before this message, and it still applies, call it again now: only a call made after this message counts. Don't change any files this turn."
|
||||
|
||||
const readyPrompt = "Your turn has ended and the review of your latest work has finished. If the task is ready to merge, call ready_for_merge with an overview of the whole task, then end your turn with one line. " +
|
||||
"If the work shouldn't land at all (the approach failed, the task turned out unnecessary or already done), call suggest_discard with your reasoning instead, then end your turn with one line. " +
|
||||
|
||||
@@ -15,7 +15,7 @@ const signTimeout = 2 * time.Minute
|
||||
func (m *Manager) signer(ctx context.Context, user api.User) func(payload []byte) ([]byte, error) {
|
||||
return func(payload []byte) ([]byte, error) {
|
||||
mine := []clients.Client{}
|
||||
for _, c := range m.clients.Recent() {
|
||||
for _, c := range m.clients.RecentMacs() {
|
||||
if c.Login == user.Login {
|
||||
mine = append(mine, c)
|
||||
}
|
||||
|
||||
+355
-348
@@ -882,13 +882,19 @@ type CompactCreate struct {
|
||||
|
||||
// ConnectedClient defines model for ConnectedClient.
|
||||
type ConnectedClient struct {
|
||||
// Build The app's build number (its CFBundleVersion, the Unix time of that commit), from its Byre-Build header; empty when the client sent none.
|
||||
Build string `json:"build"`
|
||||
|
||||
// Commit The git commit the app was built from
|
||||
Commit string `json:"commit"`
|
||||
|
||||
// ConnectedAt When the client's WebSocket connected.
|
||||
ConnectedAt time.Time `json:"connected_at"`
|
||||
|
||||
// Host The Mac's name from its Byre-Host header.
|
||||
// Device The kind of device the app runs on (Mac or iPhone), from its Byre-Device header.
|
||||
Device string `json:"device"`
|
||||
|
||||
// Host The device's name from its Byre-Host header.
|
||||
Host string `json:"host"`
|
||||
|
||||
// Id The ID the app made up when it started
|
||||
@@ -4790,353 +4796,354 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
|
||||
// const string: with thousands of chunks the chained `+` fold is several
|
||||
// times slower for the Go compiler than parsing a slice literal.
|
||||
var swaggerSpec = []string{
|
||||
"7P1rcxy5sTAI/xVEv28EpfMUmxp7/OyajP0gazQzes5oRivKO7FrO9pgF9gNsxroAVC8WKH97Rt5AQpV",
|
||||
"hepuUqIkH58vUrMKhUsikch7vp8t7WZrjTLBz07fz5zyW2u8wj9eOmcd/FhaE5QJ8FNut41eyqCtOfmH",
|
||||
"twae+eVabST8+v87dTk7nf3/TrpeT+itP6HePnz4UM1q5ZdOb6GT2SkNM5/BC24LXT1fKRPeOLtUHv/e",
|
||||
"OrtVLmia2lrJJqzhV78vH9rlldgoabyQIrTOCO2Fa43RZiWkqYWxwgen5Eaoa2WCWEsvpHP6WtXi0jqB",
|
||||
"PSzkZVBu4dXSmtqfCXWrg6q537BWYksTg85X1qj5rJop025mp3+Z2atZRfOAh/jh7G/VLNxtFU7QabOa",
|
||||
"VbPbY/jg+Fo6IzewqL/MfsRF/QLf0c9z7oX+esl9fahmjfRhgfNfyDCGwq9rZXrThPbixtmghBSNNkoE",
|
||||
"K3TwDIpZNbu0bgNdzWoZ1HHQGzWrZtaJG+hKB+GDdEHV89mpaZtmuJwP1Wyra5gIP9cmqJVy8MIr77U1",
|
||||
"i9777jsfZFDwRnphjcBtP6dP0mD91b1bK8GdHnlB3xcmFKS/mhr0WpnausKrD9XMqd9a7VQNO5LNvesw",
|
||||
"fU6LjksY7koVkbTbfHvxD7UMMIF8mWPsXjolg6p5b4tbM1pS/ObirrjiS+uuUo8DbNFhLfj9pbObU0Qd",
|
||||
"WuORF7oW9hIfIRaFNZwk+BM+EVdKbX0lpNgo7+UKn4oXjWxrJV7YWlXxGOJzW6vbeWn22fDjCT7HoY68",
|
||||
"uNTOMyoKp3y7UXAatefJRqyoxLINQgaRFl2J1tTKCSmMuuHWi25zz4S88MrgB8KGtUpd+eJsJ7BqozYX",
|
||||
"yo3nDwgrYcOPvIDDLrTBwwcIdeRFUHJzJtRmG+7gBEh8Lm5gV6zhLyukXTC9Rsna419OXWt1o1x5jhtb",
|
||||
"q2Y8l7fqt1Z5JGbQII7bUba077W6lG0T5uJ5BIawprkT/kaH5Vp5caHCjVK0FHtjui+x5/Ks7I2ZglBj",
|
||||
"V9qIm7X1ChdJC6f91T5OFEAgI+R8pBOFkZxt1HigCLNTIRl+XTfiiVdKvMWnT89Go3jRaB9gateIdsor",
|
||||
"YduQk3692TZqo0xQDsiBkvWsSkOOL4Gc+qUu6kbBR3RjzSq8nxt1+CWSU5Zz6P0V9Th6/jYNMXr1ohsT",
|
||||
"5the4Fb4MTh/tDdiI81dpBARy9MnQjoVr99KXMjl1crZ1tSA2V5os2zaWtUCr1vsAWlFWMt048DTDd7S",
|
||||
"ytRwAVWFOyaj9QPeIkMaRKpdKNPdCv1OvrdNY2+8AOqEk0QEx18RdW6kF0yB8eSeitU2HP8HtUQGBI42",
|
||||
"0r9KrNRGG929hWvPBL1y8lqHuwoYE3fHdIiGsianqTnKLfEx4kmtbmfVTHY9zf42uchF/1burzcbKjsd",
|
||||
"uq7EEk6nERd34uLOqTNhadNwXSKsHUAY7guXL0gsrblWzssQu6Gj3Jqg+1DM6HuJfAwu58GlTKQlu5yJ",
|
||||
"ApZW3F3ZSCUS5e7dolV+DeenoHifhyCX6w0zyYPbXDJgC1dGreXKycKdxy9OhbetWyo4YFL8/e9/5+fi",
|
||||
"orHLqyIWK1MvgMUrc2N6I1cFuoiPT0UtgxR/fvtTseMrbeqcVF1qhF2tLy9nseNuRbQ1ADgjt35tAYat",
|
||||
"a4o4uZUlXh76PxVObe2xU40M+loJaDkXPFtp6JeAlnCpIgEhon1j3VVwSlVwtdumDQqwMvWD7G/53lCX",
|
||||
"46nAGk/FSge8NBBXnTQrVewhLngHx+vCji3axbgCBPdyrbhRRSy9kXdj/JT8lJtfWNsoaUa9YrNSr39q",
|
||||
"7EUB60lmXFDz99MM6ydkcotAK1GN3uSmT31xtVa6+o314QW2Gy88qNuwfxrYqtT/i0YrE17eqmUBpviu",
|
||||
"zD7Ru8hhWicGbC3dWz4Ip5bKhOZOyCUeBRZfx7C2m400hevhbWuIO/2nXwuQnpl182vVNPEYru1GiVo7",
|
||||
"tQzWAVh1xyhGqRRYge20RAlIYNuw2BQYj//UTdOTcLGvxEdoLxprViVmYbAPcZW94Xbvy1vl2ybcb3c6",
|
||||
"IYpaEYvjpJkiQ6A4WMCdPiFUh1o51zs4F3dlOdiH2rbhgKZDyNBy8qmkztIEpiH1Vm2buwlZaLsVXpna",
|
||||
"o3zjoCEhlFwu1TaoWngVALO8BXnIC01Yq4HxlsKRCFMJb5ENEVcGuDPoQYP4tLGbBGGA+Gu5FJq4d0V8",
|
||||
"aFgrg2NfaiObbAa2DbAhfzWzakgkeWrjFZ2rQIKRJcwnJgb7rMTNWi/Xwss7Eq5g5bwSZGzjWlCeiwyv",
|
||||
"HSBFIsgVEEdZEmpkvaDbkibQAN/GNM7PhdcrQ69qBWwKAFivjAytU3OxlSu1AB6ldXCnijc//xBxNciL",
|
||||
"+azahzfVTEVF4RgyN1EVFVe6tG1Tm6MgLpRYSue0qqMYtfsQ9Pu+kH5dCXWrllW2PvgA4BhaX4njb5L2",
|
||||
"CtjzK900qgbeFWVvOu1lcULXZcTlNaBSpCzetmHbhvJsTwUdHt5rOD4i2JUKa+UqIQNR57BW2pGm5X9+",
|
||||
"K/5T/ynfPvgF2pets8BjHXnuB/BMOYUrdTooj0v8wbRvfmBooNLPkxYhrFVOsY+8oGnPhd0qswB8qAgr",
|
||||
"grzw/NPIa72SIb75hz8VN/GAAVbXwNfHewYZ5QnuKtJOWdcaoCObN9lBC65VQ33fWpraEwCCtTDf1y/e",
|
||||
"iBeyad5Z2xA1ns8KhKgjkv0OAWtOScrqtFl8EbCWNFgG7UH435HYBw7EmHg/Aq3rneQXUbWg7QOtHeuY",
|
||||
"WLWE0vFGOdh36a+Axi6tMWoZVI3UM1hR20ogL2GvlRMncqtP6ILwJ9x2PqaZbtVuouLgY/Y7dVTc5jKB",
|
||||
"SGgK/VzYGnUUEq6TO7MUl61ZQkNYGgjnSXi4OPKI4Wd4hJxCfcS1bFoltGdCNkC4A1imjlqd4nD3ZJ8q",
|
||||
"Mc09FaZRviWQMoyvCVCIxqviTGylg02OI2tFGhzmiXNa1Br4rWrEZx0E7Ue88eK9C23oao7cGkqvsBaQ",
|
||||
"ppbWXOoVsqweqQe+2K7mdBLEE6BI29UWUPT2D8/++LQSS4lkXHIHsmk8X5d7j2ldPBDttrGyPmUSiuTL",
|
||||
"Ldf6GoBjjRLKBHcnkF1pt31xEm9sONe4wP/35AxpIpx3RhaHWAO8gFyq+j66ZF0XeckO/gT2gHeHF3+d",
|
||||
"/Qxs/1/bZ89+v1QbqRv8qf46q5DE6BUyUuk2fi2XR70tAAWbsaL1ys35mrlSd+VLbptrAQC1kUlUy1nF",
|
||||
"wJxVs9reGP6Z2JNZlSHhrKKDPqtwUbNqlm6f+Jubpaso/o53Ufz7H+kNszH3UDLEeVYiTbMSpaMinlhH",
|
||||
"N1z3CT63Lh2Xu6eRCSTauBtbevdtWnBpxPSyMGQlZPcHoCmMoI04SXPxyl0r159LrggpaE3mZYPaxQSZ",
|
||||
"HTEH1YCrZNpaAfcFv00L+raOyUDm3I/k1SAv8H9oJ0CLLvza3vhpHfB4ftMghvbiQnldK7Y6BCcNfZvY",
|
||||
"Ghwv7chpt2ORIqepZf3Hd9/rRnlkovezU2WI+SueWgcBBFdYK5TfxYQme5foPOacrx4sTFczuKHHY2RX",
|
||||
"OEhox/g33uaAcEC0y9OGDbgsGYgikUawcCuha+htpQJp5ntMSWzkT97Hn6/qD/N0eItdbduQXwAi2OI0",
|
||||
"WQk3gWggQ92oCyHr2gEwg0XMECibGtpBoY0PSqJSgFSaPaygyWUdwIwPVIjb7Q6u8PzOLCdl8iMvGhmU",
|
||||
"DyAWaw9ion7R2LYWyDLZrXKowu+OKUBdRo0GqfNRVN9aF3xULA14wil3Bd0NO5/yRzhU8Px1fYf9Sd2o",
|
||||
"TguGl58tK83tVXFaaIJBRUJavfDtcqlU3xQ1pTNFX5AJRSIY2eRyUo04Yb99DY8TgQR0SkZva4TSOGOy",
|
||||
"eMwFbKxtktFL+AAnnUVEZDOlqe3l5Vx8R8ZeH++D7Kto0C3jX2FZLDm8SOqwoWIYGJcyFhJDAu8TBQbR",
|
||||
"/aLVDaEbYAZgnQ5e/AkoywtqvVayVi4aiRObw6iJ228QH6eUjUng2e1Qk7Ssv6qLc7u8UqETlQ5H27X1",
|
||||
"EwAgvgxVuP11/mh9XOUONnLc4avvEiQ3slai3Q5desYgJaDFwaYght4uEqWoaZihfg5admqvpMAjDGw3",
|
||||
"0tDU2Lyqfdo2bZZ0gcS59s+yYQpzGNBRyiobZJgq7vJey+hnkexS77y1VUTyAV6VyACfvNfxuE9Qgd2U",
|
||||
"n5oVu9fuJQgw4671ylhXUmjCIaSXRCRQADoV85UO9DhpkchErW7Rio8ckhcbGZZrQC/repypvLDXalKx",
|
||||
"CRhf3hv9TzVlGgSMhfdwtYLM53vIoE34n98WeRZ/t2m0uSrZvGLzMTLL0AFDaA+MOXgFALfN/fk5AQQN",
|
||||
"k9SOmLeNChItq0tpjEXNp1MgPxCI1LVeqkp4IifWia3e9mz8tXawMJKGcIgDfUHi5r+726rvsJP8yffU",
|
||||
"Yf7oF+p8iF64OzxiBz7enCph0gT+/aQ9iD1jDAQY8U8d1MbvO4MJl7t7Rzon76alO6CAuaS0QwKKoo9w",
|
||||
"1obc22j0coJ1bc1SFg0EvUmgoL2BQ8SrB/bCiBvlFGubDmIscLlVgmA+fmkXkifvgLqQz94B9IUbFvu+",
|
||||
"Ll7zEpZbPNKodVzscQt9iHU4Oigc6nVGlpvoGRR9l9CBkzzsJOpDwPHQLls/b7cwPIsOpLRDQexCgXwE",
|
||||
"0oL4BQxByDDCl0J74j3CWm2KaLOVd6glOX0/BuzWWfg5BSGvfuuBZpreTTsUjNxdf5sxGCvewG6Key3j",
|
||||
"iAhvGJ8GJ/06qoEPOujYU+mUw8HZxagrp1BfqaRrtHJxL/GIIWcBYtYBx4vnywOWFgvE88VamtJqo6rb",
|
||||
"l223F9pId1e+fWrVqB2f2qZelCmdU0CkGTehibhQl5YVmvRyAgGpt6JnYutzTZ+sa+QWN7bWlxp/4nTx",
|
||||
"F4/PQyyWCJqCz+IEIePRqgx2OTAS0Ca3ootQ6O9FB+wxUSZdjQdj5J/ffX/8vwtwyjjDf+Hk4hVQZlem",
|
||||
"ocbsyiGnsuwnsvMqwamtbYOm84Z0aeQOzRZbWNLh1wff3wyjfORq2kEFwP0d+HyNYL0F1q+4pAl4lWdF",
|
||||
"3ZSHRvXmWw5SGc/AKckxKRPsG/kUay9qiwEhF2TxR1bshrQG7OA76R7Wjd05wt0SC4aMEPw8jD3j5Xwf",
|
||||
"P+e/X6VehgBKg1dxpTugdB5di4es8y2QCbqWespfgk2w4lLfzlkCqE9Frf1Ge69qsh436jKAwThi3Gvl",
|
||||
"VkrcSAcmg5xrxYGK3OFhMMGPc5CwO3K+vD9va15kHw+SW/WuW6YHp9FViE+L4AVeoID8vds6U2T39A5j",
|
||||
"IsTfEZ3Wq3UQW2kUKZ7P8u2JDUm5QVc6MRhhT/jLeFB4WRyxSv4RnS78iUT+97KxN0BsVmlG3NS15umZ",
|
||||
"GKy46pQwXe/0J60DIgykX19Y6eoDVUuvzIW9fRVUMUQEVQoa2DnDmCz9laozJQO5GPmrBf5ZUE0+Fl86",
|
||||
"0VXG+/UXg+bY2yCevJbuChTWnQF1ubZ6iZ7zGMUlvfj7sXginuKl9Xdxgn/+R/xzq5dXx9agvPP3Y/EX",
|
||||
"8be83V9u/5a3Azd+jHBAFu1plSAKlo2AARBPlFyueQ4YuHIb0DRtXa3c00ps2iboLViugmspFCh2/bQS",
|
||||
"ToFWBL34xRNYzEY62CFe0xH297QSMvlSe/LEui9n7JS3zfU9Nyl9NOFb2ouMPIiLRXRNd1WBmz2ISmEv",
|
||||
"TKPuwcqjUiqDU/dpQQDLmfzoGd9zfu8W/7epc0kLrUt38kcBjvXjwPxr84o+/2YIy4l7cu90SzxEhn4H",
|
||||
"zzhz/y/s80NoRq0afa3cp6E06CVfMAWC57bQte9xA5F4km+9FxKEW7SSsvcLwg3IZwLNaMAhBKDl1Gll",
|
||||
"4tKD9d4Ok4+9mb7gwD2Om0VmJelxyKnUB2tr8Y/Wh760REtE0wNYSFTdg02668jilXaK+CPWTHdDo8Oj",
|
||||
"uIA5XSkzv6cswNrIg48LaPHgw9YrdyCViJvDHyU9H2NN1TsRe7UApcP7Gc7YFIpbcq9EJ4pAMYxCdhde",
|
||||
"vONOxUU8C2iGIHsznI/qazkcg3VtadrMFHhl4i0N0epyGdAhWXHoW2IEsjud2AJURWaNcqDcbxkRhwvw",
|
||||
"p2lCgzEwo8fYRoIiLTfgRRA7JVb6Wpm5iIum/2UjjA2KqRfuRI9RymBSVtk+7GwNj1A6Pdjf3iPxjkdN",
|
||||
"6QIIUpOK/fj9+TBS1G6VwXg/f6McaWOipIbhalvlvKqLOphq9pOS9c+2dDQ/JYPbY9N2iz4GcxNstw2q",
|
||||
"xG1PnpD0FtWqXRMOk+ROyjs8RVZZk3uPRRZjD6H7AaeU9VxCBZSWv5fL4CeUFJIFanuZuQDhDdKgRtrU",
|
||||
"Ym1vxKV0QoMMdaGUiZHgNVn8fYAzjYH5aIfr4uLwc2TDty15iafIhU5TGsBMJUiBBy2ecAA/uZZVQqar",
|
||||
"rUpB1Mm3rWInDlCuWCfW8hp+SXha4y/GVvgUxTDwLiDjQjWUDskwKi6cNEAf7DUPzzp5NFPWygQMMYXp",
|
||||
"arNKcdtz9GdeXAKoaXFVcqVCVje68xp1I7pdqTgmwQsWdQAKFScOMSop9PmWJ69ualWXwka2AJal3spi",
|
||||
"+PS7KJuyi2yXRaEfzJgL4zi8DlFdIzvwIbxrjIoEgsGw7vgTzoFAEQagcHnaCW+Mb9px+Ap3CsH/CEik",
|
||||
"wAwUvDmiZwzNE9u4uIjezkjxwh7LNqytU/Ux+P85qRvlCFoH3fxvOiCWLh5CwcXStlPRV+xuaC8ZXX33",
|
||||
"cxGRucyYcSPyJJq2PKRzMO6XvZDKeuTYPZ2OAoawjqo04SPPZ03VaVlEIp0ClTbr3XauK6YZeNDCusQ6",
|
||||
"OxcX209sTZdcJCgfOrYZl0LKeYn5PLZWG6BGiNXoF3tVdeqcp2fiWVI5kdu16Uc2ZqtvDSJsvSA3jQLc",
|
||||
"ya3I94gnE6Icxvy6T6r2jIluEqWdbhRvIEQbK5elG4APxYX0CvKYRMfvmulOl0ZhauB4PbBVxu+0oHUy",
|
||||
"Ek0jS3vAW0TdyYtGicLhmTI5jGA+AklxqmM0Ghz4ES4PD+34lFV9sly+p5H+TckvewXqjxAigKu4H+d9",
|
||||
"gKug9nSJowZfBuWAivu5iK6Xo70eOP9V+cM8BUySmtPNRTdkOTJemXqSG8TADk44lCYNX1RdnCAdNwxS",
|
||||
"+K1VrSKGCP+O4ydGppsScDyu9jtZxAHo9gsje1nESQPazzboS86h9sapy+J5xCAaoDq8HgMf3RFQyLWA",
|
||||
"b165/OUcuOKxPrvWfildAdrIH/HbhW9XK0oIRCOVaTk62hYmGtOp4Ps4V0v2iYmuiDHbOnvRqJJ/esa9",
|
||||
"+WC3W97k7DElZ+NTXTtqs2v2UaAtjPZ/xlfguwEWjZj2qNiRk0EtGl2+MyIsukaqPgwkTsn6bgIS+G7n",
|
||||
"6tjfunCq0MrTOsP3ijJ1ZNF8u9lIdyeewOs56eTxFb84FcG16ml5wGgNmtq8+H6R+JPp6Q8OTbdT3boS",
|
||||
"9vXhH8FWJUQf4VY+1dJJfCONmjDn1U5ehiJr9M61EDCiGyUk6TSA2WYuBsxctNVspm8NUX4m6mNgomuR",
|
||||
"qneOE5KD/o02tb1BJzL8MLlicjdCUQKqKN108znyp5FmdEY/FBowMCEyeYnbsVtlgOReM1uAdl/45oBd",
|
||||
"jGuqOjCWwd/x9eMN0H7byLvFpGfoSocFRrpNvo2fllJCJeskEtO1REbTqzAQX87IwwsTBqxUEMYm+eU+",
|
||||
"PsYD+FCzqr/GbM754oqAU26jJ3IH1iowTCZcIJCHq3WduTwQpxT11xIZFbrjpwKMyqAlpY6+lss7sXIS",
|
||||
"ZG7fLjHK9XzplDLiLV7FfZEh5w9CKMtB0PPtMSh/1Nzf+aA2W6culVNmqTxkLIq4i8MeeXGOjcQ59zjE",
|
||||
"3JKfU3/E1izXagn2yYQqoZe/wLWmW97zNtgNxYfYS/GLgx90duk3CTKZuBS1eDhfcquCHUX+NQ29332K",
|
||||
"0SW5T/HuZ6As4g/dnZ9G+Td5QIF6T6X6s1vIBSegSa7ooFBFTtUBt2ZFj2ohg1iHsD09OaEYV4gKAM2I",
|
||||
"1I1RQaytD/hCndJ76JkenMDNrmK2m4w0SxoelW6wuFOhbrfWY/xkDG4yrOiArzaW/IJIwpx/nKtlZ43l",
|
||||
"LURo7bWuvCH12HjnZFvrsIjXXRns8S2FVLjW+HgjdIn5yDAAMeoJLlVZzKXYcToSINHlPtPGCpxQmed+",
|
||||
"hMxPPLUFTe3w5FA70NepjQ1Tr7Z2MekDyIzp7r2gRt2WkG4S3Wt8fEkK7qENs9OQIkc5ADt9emj2PMa9",
|
||||
"bj0jSCY47MqNN1xxNUTHHag8JWLv2Jdp4JepY/fFjnn8mm1XfybTG/lceAnEKbZAdirfjuKdM5jkPiAF",
|
||||
"u7TNhAP/fU6Qk7gCUKVx8mdS9VKWmWkHtehoFAO4in0nv8swvtN3p38uxvfEcUHH2TaNaJRZhXUvzmdM",
|
||||
"ew9N6sztqn52Z0ZkXAcBiye3d1t8Kcax3Zn2pOTbPYhNoBzlAAROHVXLbVCu4z+20nlOBYB/O7W0KwPh",
|
||||
"UOO4uUpcqTtKfsSxoSW3KsoMV96NLHUcqo6qSPwRieYHq/J7+Pxhjx8PgzHNrLQVbxMr05/0L4bJ6Zhu",
|
||||
"Dmhv1bkMRgUXtUAjlum0nJBUax3TPu73HZwi+qjk4qa9iFV/pbfbiTQhD7kzJ6KjiduHhCfl/B6sJ130",
|
||||
"HLem8PgAB9vcBW6cPnjkgBxNhGRs4smQcoJmVC+CjcrcDLF18EIbSESrTUzSw608ZfNh/TUNAKJ0OZ9Q",
|
||||
"XD96wn3s4pPnXnnl5JnKkf6DNR+8ts68amwg5hnNmyDCT60Q9TKfwsg/oXx+eRhad/nAhmlZDGdByM9M",
|
||||
"e7HRgRV/XVKo006bFR1mn1K6dIZQRWAl/RYwVZhleSmDWmFYYNChUZUg2ami6Bn4HohvEX5977NSdsZi",
|
||||
"kt0C33fko0v1Wvu0VuHk4ZTBl/38+RMCYkbBfFDbaLEGhhFtL5hL0djeBHIptbOtwMbMqlmyrfAwB3r3",
|
||||
"E7EepB/PHn5HvWdPvo8DZc/O45gHV3l4AG7uTjadYUDc7qJf7SgZdXSvJdo84KMHlKdEiktX4DkwoS+6",
|
||||
"3GPjfAvFpGSJuYMOa6FNsMkjIii30UZC8jXMS3af1FUTnPsO+SNOcd/i4PfOBW60+QlZxdxHeDyznc3K",
|
||||
"MsTOKS7Xqm6bwtRqebewl4uNNaWYvW9EsOL337CPEzZq7oTn3qJjHMrpLIFjmyOu51LLO4F546wLmCne",
|
||||
"hLUHMzjqLG60n9BRoFi57/qKa3qJjTFnRVs4Ss9gCb/7fRUVCaTAgRnapWxQfQAzwhUaAX1kK9yVfHN8",
|
||||
"z2jTBjUxgz/88WxiDDJc0sdiC2DDuwBalYefuM/eDdiUaBhNZBoul0jVyypTdRvASH34jbu3WAYuxbWG",
|
||||
"dQFCd0UIZFn1sidyYuvsZht2rr7nH0Rxc/nSCd40pwun1eVEfqrQTFyQ+Ap4dlwcLSvwssprulHqqpZ3",
|
||||
"E1pjeIMZmdRVhhbxVFXitTVwkHBZcTN7B+ggueZXmkPJOn+gTi7uJZc/wEAsoD39wiegnWymE6zuDQKJ",
|
||||
"RRJoAyItyGDIBz2dt6pPwxKKdDdapvVJKL6LSr6M5CcyFzxgLeNEsHPTU9d0ux27Kd8FuwnuW4YNWTrQ",
|
||||
"9VFCEg9VVxNEGGC8kbd6A1P9/TcIFvrjm09PWtNAv/t9NtCzIpVK1DB99Ic/7v1ouiQQQCVlwO7OslOX",
|
||||
"rec3UxGxUzRjstvdFGHPFT592Pdv7oAGfOrDvUfhPzhx/WNWPDDESH4JHyTa0wd6Ie3AkEf1COp5AGXL",
|
||||
"2OH/M9gjnmhxMzIvxoeEjaKJZOHXsqRFpreoqHJKQavkq+wyY370zDvy4seXz7/rZTcfBhd9pObomtb0",
|
||||
"205WgPUpBO9kJU32CpgW6x9jtkWlou0N4+x9SOsS7bYrynJA9oQp2WM84TfWo/Jm4FMdp+krOijfULC1",
|
||||
"U5eUsvfSn4AG9QSaQxJJEv0+nMB3J+/Nh/va/6pZ3NuJl3q1KnF74KCzUKY+5XycbPhUpj62l8fwltFn",
|
||||
"HvOqAbhlX6dmow+HrDksmpItc7yMvFKdHzTrx057cXH0LPMP5JBq5VQpRG4O/V+ddkjRm80lluXL4hvo",
|
||||
"dsEM0FeYJXTeOfbm6xad72t+HoQ2SLmk6T5D4sGnKPLGF2qt2V2mb8qsemtlj7iYr53mTD64OghtIoTI",
|
||||
"B5kSBaVhE1qhq6ABap+FZ0QS9aTrmNPAgx8RLTQPzAJY4EogHhv+WMAfOCQcXSfkdqukS1Ec3ehID4CH",
|
||||
"jKlwIIci0cDId/FkZlXCsVk1S4PMqllaFVdHcbNqlnAshR7Reb06UAMU6eg7wvfXaQ6DF+9aZ16auvDC",
|
||||
"2uYFzW/w5rm/+pFnO/yGJz94/DZby7CvuLTB8+9xpXuUQwU9kJn17oCMGOTEtiMDex0BzoNcqechqM22",
|
||||
"cCdN5djEUp9U7SHPIh7zyGApLXbZk2Vuj33tkiV+WCeL3yKKLhsljaoX7RYogRXdW/AWwAMdE6xh06l0",
|
||||
"9vTZnjiF76hVfusk5zgmAGAlWzbWq1rcqUl3vB2FSLPUGRruMIQ+jkifIXkcZbFGwCbJmb8qrlSbbTsV",
|
||||
"J8TfeaZptIk+y+OJg5P7xlY5JiY4NloIgFStMezZc025I38wB4zY9gpmV+L8pqqevFtHfFqwJQ4bdkDx",
|
||||
"PEsdxEoN46q7hMD+PnpvVlWfZjFS2ouNrvGunAtdN+qUvYGqaCbopsiegMROpdPQfQC98jKIrSSy7iD+",
|
||||
"q0Pw0+5nvFVSXNy8dy66dgSK1AzT+uMZqa1KeXlJsdI5JWUp/amWUe8Uc/6MsRafq4zG5UXP2XqWHXCg",
|
||||
"QWmihxYfJSQd1hfNnsZqpNmj77tZ5I9fxxnlD7/LZpc/f0Ez/fN2r1EgCZqH+mdFwS1q7jNylE7sJIWm",
|
||||
"M1PKT7Qqe7ccnquPjQ7xg4kphJJzMyHzAj6dcEagK4zzwZekTFRe8PniZoStGsMVl1eIwoBnXf3cPE7p",
|
||||
"sJD/vAZ8KbEGJXsvzI6VaVT1fbsl/3cSPeAEl+rXdCmdK2Gbmhzb8E0qTXrwxIdJsAtzry8W6MtyYLa4",
|
||||
"rLb44YkfIfBzMVW793m+d8qLZescFQYcR/VlfbI6cQJvtuzfsegCRg52B/GFCIuHWN7/jBzlh6qEE6lG",
|
||||
"NcuuLWmxB94xVZd8Gxukmk3wKqqzOMkVe1XTh/CJai45BX7PrtidSgbvAiMzCrcmPE7Ia+xNQsdemdyD",
|
||||
"cRH6e6suSzjovVpkZ6hoyHb31R340C6vOEbHq6U19UThaEzYINNKUZSFLBArm2AshSc/KNIx8FWKSeJb",
|
||||
"TNIK9f/bXiHcbPb/tJsLXSJe55EicQaqOAUYVhgrGiw1SyeDeAQ5OCxI6rhmTtoW6w7elP8Hp7bXASqD",
|
||||
"f0YxRtQgO5VVn7j397ijmH3aMMTJYaDN8DboQFve7jEdKN5O5LXxVsmC1XpS9+TVbylOpYtdijoodKKl",
|
||||
"fkkSgPiXg1RKhVS4pUm/I4fzkgN2WC+KlUFg1hChgHcQFcSijBc1xvlTmIAOwihVp3JgRVNUsZDAdChG",
|
||||
"fNsNhxwjSUS4jIizo46t0xzJ0u+WtU/rELZeUKMuRhq7HFf3xpHXWOlsqRzHPipR243U5j6s/TvuDEyL",
|
||||
"cnmlqGZmUKfiZ4tcYCV+Bhj+BADk36/lcq2Net6GdSXOKZ4QfnApJuvE29FtN81xIQOYdjqDf4JYGWf8",
|
||||
"VQlh6qkyGIPUFyykg3oLvqF/fdULTsf64Fhm/pLqEiVvR/omGip60mkmRxT3oQsL2B0yHhVscYZHXPHe",
|
||||
"ZfkimQUj8arTQcoQlIEuxZ0Kp0BOuxRFGN+3uLRuQdo36wTFmXqLCVCeYAcUiMPhGndx/d3A/imlRQE2",
|
||||
"sEuK0iUwxclxRLNDYk+5KnUQmzZ0NoZC3sqsnqaStVipQBeCXhnZ+JRdtqxpuJBeLbja+Bjy03EMpMHY",
|
||||
"XaaE1KrSs2RKXsIsOR1eMWNpm5L71Ct2RGSR9M+vovvBVjYqBCwiybUBJVvuMWuTFNghQGgUjENuA9Zw",
|
||||
"dDXuD/AH6K+5suX7/THiSB5H18S1KvhiQk5He3EjNRKhqCjKOaEq/oUuFjVAZjCCuFDQ2CmIS6p3arPk",
|
||||
"lCow03aRPjCl4+iyfPQUXHIiT8o91GawlF0qs31Eaco/aBQavmsHOWsxwCD/ZNeudIlQiulKLCtddgW9",
|
||||
"T6ll8W101fytlT7R/GLk1bDMkSOAxnREwdKxL0IvS4u0j0fNclWlDynrwjTxYbhiuAgr12OsdB5Kp3sk",
|
||||
"NVEqbVYJFaxZqiz7CR4zTzGrXPoHNmutmnsQNFrDIc5H452glW3klfJZdT76oON1YhhB8GnB7HYcaFw/",
|
||||
"uSt7aE4H2Zzi8H3a6Sl18Im+5BQk8suC+GX4NqpraJ3YcNviD9beUyxkXOs0YoPgANfBgszVu1ax6ZKb",
|
||||
"aXToZnTgiCTqYIA/ke7xS14y32w6TOvy6YPF/kzYJDfQ3kUHukSros6c5orAZF+sjreBBsmTq3PIuxdd",
|
||||
"25vUGJghXHV5JfiqQ0LyLKewgBEjRclepM+Jyylg+rZRQWGquc7lIGGD8gkZKqwZrE0bG2uqa04MVi+J",
|
||||
"wFw8jzW7gA4YO5oOztPPxauQUhqhQZPIs+jSYFSinLqkytosQGmNbkDJoh23FHMdXNjb9AClxtwkGiFA",
|
||||
"hbtwfQd7tsv6Ds/oi66P/GHsbbd4YylqKajeTQ/SzJXahlg+lTL8RZdblPCcNJ6KscG2UuQoAZPcKjCN",
|
||||
"HwCJeseVl3LdMbNMtqpOa3pa4iSJRhOXcxZ9rManoOqMiomog3gQn1ad/gUxBA3gIy6+R9BU4xUql89E",
|
||||
"x7GdgVjRdaMDCZ0GsTCJFWeJyOFkmLAeMdWEL8YUMn6hQ6SWyXDKZhmMkU0wju0JyogjmbX16Vm87OLV",
|
||||
"GHM+dhRySBG17zQBnMfvRt5RR2oBt2BatU9XBbtUpJsodp3fvbGLHhfUn9aYS+q6hv/p4dn0LpKWKTfD",
|
||||
"Ua5CJKr8kj8+y0Lv4ix4UPDYja8XWy50fKHCjVJGUI5K8ovBrcOt6qJeBmkqYXxinzkA5IyJUrCdrDki",
|
||||
"M52tLVLYlBFtJKjyRwvuhEtvEqU76w5EdOGCxYlRnQW6l4b2STJOnjH+G7tgRxLuirWSqMNMfsKHmAD7",
|
||||
"oE0P6HfkUKoZI/fQElY0IMbJkTNJ1hBRdlbk2qtZfyu6LrudOJAog+JlYItMj9gQmf7OrJDdZzijNwkg",
|
||||
"gxeDh68TiNKjNwlW6dF3OdB636q60K7/8Gf7nMGZHj2PcO339SPBt//wOcD5bQRzthpZ372zrxnawymc",
|
||||
"d1Av14VCYb6MAvfbp9Y/j311j0agaX0HG5wRidWLg1TGPc/FsQJ5lAIrpZVMJ8uog/TJk0EOLwfq0Z5r",
|
||||
"HN0VOgylhqrvSocccuRiMoNU2bXa1nahi2VTRjLPu1+++2W+qbEGSH+03MvkXnztdPzD91McA+si5CbN",
|
||||
"KzWizCz/VLlqSTxZqQDYEXM4JEeXpd3ePYUktl3fkT7G5OPRh9A6lq7FE/q/e7dIEaFRZ0dF5mAjn56N",
|
||||
"GBXpVC4xsAVJh8hDRyYI/dMhRTJqzCKDhfvMisywVpvkXPL8/iBfTASifj+UX1B7nG/EeAeoNV9VZ6Iv",
|
||||
"W0UP5Q7M955vAvSDapuwj0Yu4Ccl6rD/TPUadZxZBqXpLCdR/99dhaMLcCgJT1xwOcXqadn7+pkpW8KL",
|
||||
"Lo3roNid9BNyRVQnOXLCBjlHxtiM007Ncgw99BTjfCbkhN8spppICoYtrNC2Pc4LxptIM9DcoyZMVm+x",
|
||||
"VHvAqdLCnUqL6VX+uCQBidRl6MOM2F2nheyf/wAlEfI8j7i0ye17aEAHsNMcrYG1MDqn615QHuesQ4Kd",
|
||||
"KaUafZUiImqr/Fz8bMM4bvGj086+y/hjbEIkjUe27pBUqtNRReekBxxdnFz4QfuorxnEKebCMquWJgBw",
|
||||
"r2BFmgK2mItf+SLWXU6zqLeE6z6kHHuda+LUfT8XvfCpboJ/NaUp7o4xjGRcDtIVAfaAIK6ND0rW0ISu",
|
||||
"VM570jnnx/vyNAnvaOuS6faAFdkbw7kU6q4OB/5Nq0YtQ8x2IEMEjQ7iQi0twSHTs3FPf51RQjf8E3+q",
|
||||
"U0GPsC969NdZNRhTyPxKolBU8S4v7zO+s0rA/TBxit9xhHzhHO8Oix/gM0TXJ7rTu3Bxr6Lnke7F0+Ba",
|
||||
"p0zn9/bYuV9RsVHmumzIKZL362ExsIxPxODl0U18D1d0uPFRWKtCDb9agUAbODPmzqA+HvS77ouDcxfw",
|
||||
"wrPBigtXcvNabS6U21HXfw/tjEkAZOMtFuihQzJK4nRIZs7YMbRIwVBKss84ZoKwlwKTEnzz7BnoA51c",
|
||||
"BuU8ZJ3Xv7WkCco+vBfhfoe0madgb4jquBZVlp7sFs42Suzuv7wRMRR5R/xePK5QjH68HTHdxeH8SI8E",
|
||||
"7HPj6rrfNbVzToU2DOWgWe0OPHb2Zm+rUXKtxs/4y53TguM9mXWjaFh5PiBeTNEgrAjuFi7gkm6fcmRx",
|
||||
"kfDa2o6nom61D3uqGgxcasgdKUq7MjD+2QlLUkKJw3DD1hbLlO7DC5547P9vEyuOJU8HzL6lnOHSi3Gd",
|
||||
"UOjwiHJGmUBCcnIUmMz0xx9dtg7BBhTBF+8Y8omeYIkgko033CjplA9irWTN5ZavVZpfwYw9rtpxEBsW",
|
||||
"J44sH0y789KFfcWqpujqyZXIInQ4yfN+OpMkSosJxyMApjZs4siAuNj6ksuvanLzcRZfKbGWAYVOtd6n",
|
||||
"2cOpWa5lcomVhqUZ7THSbwKPp2O0DzWK4+DBEiNyKOCKcGK35ILn585I6sMSPu2eVS80r+Nn+PPSbMmn",
|
||||
"fDRXp/6Bbv4l2nPX8wv3lEsBBU6sXBMzR8voYi63aHRQaDysxdKpWgefVG3JqIW+RaDT4lzwMiI5+ggC",
|
||||
"n9A5R8f5FQ9UvwjahCcZlU7yJCVziMDhDhc0RV/2IdsqpE5io4jJ4Juf6lEBiq0pSd7mYK9q3KVfccy9",
|
||||
"tDdbfTfPya3nBFQTsXmYIivddijccPECEnpS1EE53CASwtYrvzuiwBcyT5qgV05e63A34S4o27oca7S0",
|
||||
"tbrdf1q4h9i+6g05CTHehtGRaeRFifM9X1tH4nByvEK8KKfZ15sp11naySM+Dz1Ete0FEnFtgJ8NgxKD",
|
||||
"FxLjy1sT0g3J49IR4GyFKvjdBwZnLajl4QcFJjTutPV1JYK9UsZXUWuBPlBb5ZZYidarRi3JFruRmPG9",
|
||||
"fNSLFSaeb2C5Al/uANQ0PAjY01Ee/Q2hrC44EPvzLBWAPfW/z6ZSzZBVuDL2xtA3o8IKiFy8XoZqGUNL",
|
||||
"Qpm8kXelnQXCG1yrKnIIiLkQYA2pXkk0cifvNu0Tn0tWAXJpK9/LMen2DrEQX8VKIFF3gqn6sPuJOg2f",
|
||||
"sIrGwcUuqpnJKhwttrHE0S7KPa6J9DE1M4ozGEK5ov2eQo8/4/0wRpIHA2ywnoOLfcTsQ5nFdYM5qkML",
|
||||
"H98gpod1CyfL6Vk182St6KXh6mZY0HsU5LcsP2mn5YjKCIqjzFToF8xow3rY1Tmora+ifIdX+hwiLaj8",
|
||||
"MmrjqlwhSP4PPuoRWa8YvYWwtyz6C2xmyYfijLioHemds3G0Tw4lVBCyq2aKOkpUa8KQMW0GfuaPUl79",
|
||||
"Qo3PHuzeF+zdK3sMD4/B1+Q41i4+xnqGys1OgbSg5k5tD5cx4zaeB7W9X3F8GqeIadzn27ZQVwb3bKr2",
|
||||
"wkfp3OJG7hbgC+HtdURIFd0VXWvKBHaSiNH+lhjUqDbgmR55RuODs4Z10MRQ8KLd6rBgdKYTnV1To+0y",
|
||||
"A3zPkYfXtGeLz0NRpEFT5QSbi/2OE1DAoamy0oGAYEfCr+X2npXQY46LaWMLVzJzXHfWh5RNB4V/Tuhg",
|
||||
"3RVZy8LTFDx7v6DZXnqVwkx34VPJt5ODCjDACWkbOC2LX5JLHy8mJJ859lDJ0rHgXKiSfa9JSkOS/OKx",
|
||||
"Ohh/2M8AwdPoAj4fMRcEghD9iH5No3bPOo+s7hm7ZHUPMp+s7mHyB+oe5b5S3dN+Tghe+mJKb5XlS/HJ",
|
||||
"6DUEf/Q1PxSly+cYh8ocCrKpZWdg1/FFqj/2LMrq/tvLeDhSdi3iIK1jVpXOCqnMn1ZE3ODtVjrZNKoB",
|
||||
"FQN1kS7FeOZzl5CstmA8/N1NfcqxMj50tRgk8wMVeymnakKDZt1ECnfuVFKDty1H11I/yBkEJTenlPhd",
|
||||
"NskZ1fRy8yRzJ5ZAY1+BKta7TmwJ22T8Wjqqa45hgLnLNGc4I7zJpnKPcteZ3eh+Vnde88H2o1Ox2obj",
|
||||
"/6CWHLJpxAuQ8iuxUmBL6N5aI553cn/cPHYsJswy4gWqCrCLgQUbo/kZ73pZRaOC56FsE+JSyU9AbeOS",
|
||||
"4uFmP2je1hVVSCshNnxCOHzv2z7yY8Nti8hccO9A/Fc+Ft3qfPqx9ta+ueYHjqNk6NR8spnvyAndx7jc",
|
||||
"ondGuxu3HGaWv2b0wddxJXBOidYk+oGn7mOwY0clhnj0gSg/yAQ67TRy6Pw+7CLx5CNWKvC4Q9y4vwm7",
|
||||
"66x04XCeih1VJkfjX2kKFx+4nC+YCsQ0EkTS4Vd6w2r5oti67an88xQlDzUXDACC004FFMew+ID52S7R",
|
||||
"/MhsO2ZDQLuB85yMfv5s/gyGtVtl5FbPTme/nz+b/x4WK8MaQYe5h2S90eZkm+p44put9aVzhkq/mFxf",
|
||||
"Q942rvrcK7UJOUlSCAZM7BjycvojgbUk0z3gr+LXFBnCX2PlmDsVRK2WGnOa/YoxHmR1oA8oLF6avNgl",
|
||||
"EYeKs7f+4ZlgXSCcCcAXbPWqhor6MI032YKrWfTEwcX/7tkzMiubwDGEUO2TVTgn/+DyYkS0DpaSu/EK",
|
||||
"vNmHqlAyigrSiK1yBJg5fPiHZ8+mRkprOOGSVthvtsedv/yK0ob0wfKDCpyl6yPhsUeagREKSz6ngptr",
|
||||
"JRt2cUYTxXy8Dnx+vMzsIFPr6RtMHnFZ/YE+fNiT8qmXymlO7Yt5C89V2XATv0U4OSWjEmzYTK44sUkf",
|
||||
"NKRRHEMHdch/Ymv+YwGmI3V8M365XXk3dG7zeMi+fdAhu2jsxQ7ieR6sU0i00OsUiV7rFdW8ZudQYt/B",
|
||||
"tJ9cAuUNtSfnfqAb8KpWAa2pnaoD8zEhOmwogbB48/MPlfhfb17+UIkfXn0vrIO0cm/m4nmgEnp/EK/1",
|
||||
"n0gYGGJHTNS+EylwWicrfdnfkGQ6udBGYn758W1Hn/5jq1YP/XZrHvzpjbrY3vfbA9D2m0+Gtgj5KWwF",
|
||||
"LKoF4Nq9UbWaffvN7x+K2Cfv4b9X9Ydd9DbhzM4D/W+GOONNBEgeeTqyVZdznEGEh5w399t7bBdJduTC",
|
||||
"MDv9y/uZhvE4zoRY8Rnt4WyIzVW2puEK/hbRYJCkMsODockmpa+kmv3E75FOg1Qerecyc6/l8jSr1oQl",
|
||||
"FbygVJWcE5vcuJhA+hT+QbNJFtCurJ5Mn2+bu97HGGR+F4VXFGx1jc4eGOREliHYnz8Bz/qjZXcx5fK4",
|
||||
"aewb0hgbtNvkeTmT1hl6XsPnTm0bueRPQfuLOdVLNJfTdNLUh0fom2ffjMF8TnmiUuBFB/SY8e4j7rK4",
|
||||
"1+pWLaevtLcUKpx8K+Em4315LZdCev5rcSH9GiM9qP7J3ymDPr47hiH+jhkXKOtcyKuyo8cB6c4ulHTo",
|
||||
"jXOlkvkP+yHdvxF/+r/fvly8++U/X/6Mg2RGQQ4VK8H95a1aZkD/9AwQdQ7DfG7upxv5LUoSU9Qobh9V",
|
||||
"erzVYbHEfIBeHH8TXd89qoBAc3alm0bVQkdx5PefbL4ZIetP8mdLLmqMWtrHU8dhUjrEwsNyCR4Zjao5",
|
||||
"/QhvaFSV/C7KhN1nPnUVVT1wZu/Qe6VwHvydofNQYtbfUnEXwc5iyclYbregknrR2LYW0APpl28w6rNL",
|
||||
"O68H2uDQ+owcvYj0rh6RJ/aj73oZM/zniknLOSzgMTEdBzgI07+dgqCqPwHlwjQkl8pBwRT+2WdeSgni",
|
||||
"G8624NQyWFAKGq5aNLiS8JqqiC6t/olpG0SQDt0RmOBQZoyorYcNmgsUcWN6jBw/B7m+4CtEw/FG/hA3",
|
||||
"8h0v6n5SLUz2k7Aw0i3X+vqx2JRux+7HqkxI0RObK8BRvbC7qJm6tO7+G/x8uVTbwNllqo/a6DdtaaMP",
|
||||
"ObUP3OOHHVesw/8ALIjHFZMd+MljSUqhY3SOpKZzgaoU/IPqcWmO1lO/nYlaBhlF55fXXFfsf53/8vOc",
|
||||
"/oxlHn6SPhzjk+NX34knTIPp3W+tcnciIelTDA9FTu6uuyUwp0mc0a9UugjVuxUljEJLJMdkKh7ZKeGR",
|
||||
"eUQ+tVGy/j8Qo+MXscM8SIUqmGKxJKpmxMvALsB+eWOUEyvLfVgaGIuQFbgdKuVPXczKB5GulO4o9iA1",
|
||||
"q0pYNZ3HmPtEiHZdJqPzx3eVlUGepAoTnwJQS98lVx0gJ3tIK0gXhMLHlB28f+72UlJCUfr0Iw4RphKb",
|
||||
"PEOv4C1GS/iBC4r4XjeB+AfnmGmxRvEJCLbny4auyYzR4IBWwi8IacPhJpBrsAW9xAn33sCP2Pvo7nAY",
|
||||
"Y4Mr4ur5ByDFJzAW4IgTcVsjJHotw5LFHLXxyZStXYov9nPxHTmcYgQPJiHDxvMRFp2QQH2MiuNpye8H",
|
||||
"fU26TGqu6uR+DNeqhqR+nXQO5iBwhIp5yyiomvOuxqpWLB5Gq3N8SsXD8ppo8JoyXMsmDgBRuMXab+9i",
|
||||
"EXFQPnCgOGRiiSS2y/AsG8z11C2oC+OPYBxGzCdnFJ5FKdyfppDmG9PotSbYFqR38E0gFwTy/U9gRDpO",
|
||||
"yXvjlKoO4KJWcAP1XBoS0GieKQ6rB2VyeCiKwlRo7Dlu/ONICIjWbxmanHviM+tWMQH6BE8bEeUhitVn",
|
||||
"396r9R8fSuVPepApH87nrBPLy/cRbdDkg+LbC7ZFzkVs7KjIdV6PFZpG1Rk6QGPIF1XSAQwzWWK9LPrA",
|
||||
"KdGaiLMlRCMMqONF8ciYVj+G3uXTU3FAQA4ES7T568XC9zDFPcaAbvmPqOTKYFyGKUy0U7F3N+LjCK4E",
|
||||
"lofp1/uQPam1h0OKx/xRxq0myMd3NLAaJyzt7ho83XSfkwoLvRYT8TCWUuY71ulCVAYVIIUeaqduuBBC",
|
||||
"LIpnMG95U59x1ym3RhS4SU0mQzcXkHnaUKIvvICOvnwp3OMt5BM9/1xHdKN2HcvX6nEdIZSbVDRnSRf6",
|
||||
"Tg8l7wSe52O4JKSQqs/uj6DcPtrPwHmo+nOjTmLs4LRDyU0WzJJvCsYk3ci7uaBIQ/g9iDQkL5OO2TeJ",
|
||||
"XbhKuutSsGElgqKBKFUxOABjY3RFaPSVau5SMWO08bXOcHNrpgIdmWfH3N4TTi7PARqPg0jY9X9JFCr4",
|
||||
"Vu0+zkd+4EdD5SK0Z8MuYBHrSF9yCJyjrF2c2oXcYLrqPBiFp8M8djin7jH/Oz+pFaUnYKVZiJkreD7R",
|
||||
"oAKpxChL4ZTG4hy6/lReYofF9GQjHsocjvyUPnzobvCBMRlFq94gj3MC8iHg9+eW5vpwPABun+ZYnLzn",
|
||||
"X8wBEyaO9+E7fD7ah31qdfqsfiQWNU39gaaVEpH9alHt2b8Qqj1U+7tRJxxOfpwicqbv/tAL4B/Q8B3x",
|
||||
"/KVtZ6Xiaw5if4xt7w3xX/KuvbTL3U7Z32ODR1wnDVBY6K8xAcwATZxerYPYSqPIE3+XB3N0iugySVGB",
|
||||
"u+iG1fV1lEoFVhx070SQF0Vnhg4mnx7lMnB8Plyb3IPM6RMx5XOSFTCR7bPQFjhB+Iy5sr7BqcJQSgqq",
|
||||
"vFnbhupAYRErrHqE5tdYHsr/j2+OwjpPjJ8ZAPinV7+h8TZ7w6HIsQqrMvGl9pxl5+lcvIFJoIPZBVh6",
|
||||
"faouhdNMxmXoXsYYsCnu8Scl650W1YHtiXr7ROZPrpHbdbWRt5R18Q/PnlW7czD+7RHRGQHyBlMNjFH6",
|
||||
"zxihg3Pnf5/wBSb+8OxpJbZN69ElE15yuZKuokthR+clvI0eozs88tHZNDnfR9NRCZs5RX8q2iLpcay3",
|
||||
"XMgbyCJ0qsiZl5yBh3FQrhxH1T5Z8Y4JTMBxFZG/gHev6eOfyIT9GESQR7iPkeZ3n06wBmhzZewpmtiD",
|
||||
"fw7QGwBisMK6BFssLfwxdzQilLFBTdPBqbuyQx8u94B15LAcK/sJ3nEavr5pPq0x1THrqbE3GJUdc0ys",
|
||||
"FCzZNn4+SaJ+tlSv+/GF2zjaoYItwnU+CfGT9/DfQZJWGvnLS1k054cZAuLyGbvvf/fG0o/0+RCznlPN",
|
||||
"BXJaj2XmuMCgqSP/T0lTb8M0Rp2rzxg+2ScJB2pMeIJD3Mozcvl9mlJwMwdZKGkdsxpyeHlVVAmaIhOc",
|
||||
"DIry/eFR15d3BSWrYfcMtK2QCztcVFNS1jgh2eOQ/FLis/+C8tZWGnWchK6dMkuuJyfZRVNxTVRMm4H8",
|
||||
"Qnrr7Js7yg7WGrzXMRIFXuW8wR2ay7MuwZ6WeAYuAyqNgHzJpApv7uLIS+ljoQK8SaxtBK3jooRM5yq8",
|
||||
"kUY9puTU9f8FHcA36oSL2hxjYOzOeGdq+BbbfSRmy7rWlFrhTS8/wSHlucbx/eMqSeifkVcrHxEW4C8c",
|
||||
"4g1zzrQ6YS8Jp9Z6tSbv9d+wXY8usug9DS4g+29io88SMU+DHULtnzdN1B0cohmPPT/SKaDev4x/UwLa",
|
||||
"tIsTA+ojjlgE9cl7/rXHEySH9yPdHXvW3V/zp2b2EhQexu+VwHki21qH47yazqPNo9qjLsapZKV6MNk7",
|
||||
"sC1kpKPbqCsdVq4Uf4QqZeUDFwCLmaZJAYBDgK3uci7oDkjKwDlf/VXOnUoTZ5Wyg+edx8rca1ljwGVy",
|
||||
"skzNO79mLOhiHURr2E2cCCZA8lWs0LfSoZeif3SzPofPUmmZR6UraZTPzJ3tOWGRQXsodXmomrJ4eChF",
|
||||
"6tdxegY5YGNOdnjoB6gYHXh9F0uMaiBrlLCmf2JiD7uOTBFVqejqf+Pq14KrMOe6bdRuMT9Dkti+Et5a",
|
||||
"o3wgJKE65ijhxyaZ00R6VPKauIerRJrsZ3GT4NEOFvjj7L6ue37K0ZI4RC9kmjjfOyPWHi8n9qWU/dso",
|
||||
"nf3+pheyXpFTCLd6LCs9d/9FnEEStkzzvglIX/TIo119p6D1Dlt8jjNGARGHna+OCHE1YWa6iOz8S525",
|
||||
"rGpnVlc2lWZN92+35GG9WWwPCnkuJ5pqSPpoLbKuXzGzEst04uGgYwySDNl7ymedV3DGXmk6uujsTAvC",
|
||||
"XXycI53Vif33i9XZd5C5CNzktY1HEDY6q++GTKG1KdnoNIppE8PWOdv7SLLGKnSPyEhh/zsiKzxmDPGA",
|
||||
"915cysarTsQq1LkzNgLiX0gSh03uSPYX5BKoIimGCebF8SvhkAWIPgzwLFqRU8H9nvTchTD2qvRFzmMQ",
|
||||
"wNjJzugTTpmLe5rItfRd4iMMdSjGnksXUg3BR6JVqeLdf4cVHkrYWJA8eU8/AOMvtQFs8ifvtanV7YfD",
|
||||
"8D5+fy+0r8q5OnB8cAizHpXqydqCYxz52CQWe3iGOabHc8L5HzKhnlvMHkc2mddFlHXtFMb4yDgpyhlf",
|
||||
"WxPni97vXpm6i3/ISixacalvhcUwab0yluOqyn5vNMI5x68/ivtbNsSXCZMhDcXUcSKQfrZ7P8mUJ+/j",
|
||||
"z5EvQsmxIBfrzpiZ0yGWjwZKXJLd6uiUUJVdyXPp7cs6OHTA+CTqsrcxU14HNXTAw2TzbFtXMOs2qMxB",
|
||||
"CqrsHCwBs6/61yYBP/tsEvBnl36jB8bJe/61x1AUHTweEUaH+JbxZB/r5ERQPIxHLcH0hD16DruoHziB",
|
||||
"KQb1OdVCUpQYgDs/ihwm8auQnMNeXoonKyubClO7s2+SDzIoeCJrQVnVeiHH6G7TKP8UnCYUp8VA6zY1",
|
||||
"Dt12UWLADRetxRQYpPPh19Z0TCqyrcwNJx44TrKUx6OKnzPNUHVXxEY1XDbJNnW3/nKWT9ymHM0fIQ8f",
|
||||
"DfL1um5GOOM2QpA3bsQDbqqHsrzFI6RNUM612y9ziF7GunFc2Jhcf0cI9CpOcpJSft6tTEDDfUxJab7s",
|
||||
"TvY8vz/3Rp7HyFYU+xDDUyKh5KIcVSQZ5QLimeKumT4lUor9aB+94KuhB/lvrWpVKgawjOWeODQbwyo6",
|
||||
"n3JrGzQuoIJSdp6eEygHXvLsDf7v6muO4F+PCNXXJslDuRijwk6FZKrsBw4cR7HCKX4nDNbK6oX7U/Q9",
|
||||
"lviLKZlZ4AVltaIaqGSXMjYZpSrhbaoPnF+7kBV6KY3wekVPDWYaNllBVc2piORFo+K3PMHSjQo6UHr7",
|
||||
"qGpQHmLSg71GaQVgO+/viL/yJ+/hvz2cb1LgP9oKphVUmXLqkydyxZU/jNHtA+8E69sdRtIfMuoUPX9j",
|
||||
"PWXNo+p9yWgEs8lNtcqdsh7Wp2J80GM0tccAH77m4RX0OlF8rBdqhOSHaiQRjx1d9fGgUJvoQtyVmBsT",
|
||||
"/NL5geW9s39C2D4Odce+YZj70PdvJ7wgrCdOw7GfcIRq+GpJcg+H2btikkK/zHLWm9xGmQyTT8jBDf3h",
|
||||
"0E5YV6I19AuB0Zrg5PIKnsO81BKRaL7SAXWL6ilXonRKVRQIkiU7VAILrh5fSK8SOgIrktlId5usos3T",
|
||||
"a7NkRmelr5WhgKa4DL54UDvbpX/ihIiYTbfLZYhzgKVr1r3GV1zmT8l6/0KovAZALq1haua0PnWtbevz",
|
||||
"sXAOuCAf1BZn41o1eSf5qxe82SM6OSyXwRMLNi6DJsKLi/vQLaiidPi4jxgPETnBGLe6Budu8bMNQjaN",
|
||||
"vYlgiYtJuvJhVlTYs9m9tPXPcwiZU45EiFgjUxovbfp7J5zkKAkun4z5lCN6TE6Qv57tVOCPJtlDqNM+",
|
||||
"kIu7TWWc01+0P2CNkC6qNDo7mTXq6eSEg9p+bI7hj7TcMxIWrv43yh2jedkHGb62dIOfmbmAlLU7+ebW",
|
||||
"6EutasxtCyfSGk4pf6HCjVIG7nzp6VruPEhWPUown6IU38HohwWPS6/ua18r9QNT/BT98IZ8dD+2qRfc",
|
||||
"104e7fHyL+hG4TZMuT5BkuV/9zPiJo8I1KvsKitEkY9Tf8UXWvmYByJgGH9KCWYCvoS/auRVMCyuUZdB",
|
||||
"2DacicS91EJxW228rlWRR4JvtVk2bWQQN9JdlVPRpgPo9l3T543062OvoBFFI1x427RBCeuEU40kdwhb",
|
||||
"nJGzNgU8WMdZC9I9Am+nbpAveii+0w6cEXeUAUl7fuTjzvy7HxKs///55VTKyJ4hH8uUcDBVDfvPAcuA",
|
||||
"ozdOh+7sxEDBKuq3YtLGrvrTQFGJSVe0YS1nLwuGjnkODIgapqa67DfaKyE9eqxHqTmm9MisOciSGUvf",
|
||||
"0AtZk7gjNkoadEE6E7WzW8oQgukdkDM+i9G3QxsRpFnA1LM68ChOQTitL9MOU0dhJya7he5RyGZK0n0o",
|
||||
"eLcV193nzkg4rSJY8UN2t3dqAwVZBz5VqT/t42hdQvoLBYOnkVBQAQizA5ZvL+BTf0bl17AbciNdAovZ",
|
||||
"bsWl1A2Jg0Q5wzq9m8ieCyOVFVK/+2wKqYoTv9Ayo2ssg+GLWI0GJ/2AvEiMD1jNCF/F6v/Cq9+GaRko",
|
||||
"J1E/HVEjOVyXNK9cvQZSOEBS/37XQ2smIRSJX9Cai8XGAtmAlHRJxsGSeBh9VHCFqbg/vx10KYN4Al15",
|
||||
"C/hHxl54RT5Q8OvpGad34iNwpdQ2JkBA5TNW8ZgKOAE0uU92pU9YWwbTOJSTK33z7NGzKx3kvI+QOcR7",
|
||||
"n1Iu4YriPmQ5l549/frVvyBs7Sg56tSqbaRjf+mc/5yLd+qW7QpwKag6ngDh9T85W8eZoNJY+H1sKYyN",
|
||||
"NVl38I4gOTw687iHPbwX//CoMtQL7miCsgN4/70ZRCwlPI3IqTr4CI8pz/5r6a5qe8PNyDGOvpAhyOV6",
|
||||
"QxXJYPqcH+1QPJsPCoZXWC4cBzj/v36oxBKIOApt6jYogw5S06fi1YbsxYdUgfr0OPyvWmHaX6/+x+2m",
|
||||
"+eIFqgH7YoHqf+/jirLF55fmUvQ4H1KcxiLo0HRpCq251A4UJviYdSpZ9i60uLOPOUpS3M2xNlUSbjSW",
|
||||
"nL3WAU8w9A3KcPZnw46LjnfCAGI1TSwP4H9rpV9PyFTaBIuCaDR0QLd1n1MdrNGafdF3bVhTJfy7zASb",
|
||||
"KoDb49jgGBo4EIEcElBMo5OZtiADRGztK7FtcRmaUwhC2a5KXEINxEvrbmTMZNtNbLlWyyuAZLAxeZPp",
|
||||
"SZgl0bIgSuLq6+QBxFE2+Er3gNETEpNhKOU6jFajJwRO8gxaYC0x4VV4mscBobRsbNeDNjDKyinvh6NG",
|
||||
"sxnDbKAQKGUvpJ2EuDB9y8KHQJeR1gTddCtfNhZL1A3ijMZOT6wuS7ONEjKsOxXhxFyIZKipWB5hERmT",
|
||||
"ZxHI+EkX/MkwVTUhK5wK6mMQJMrQpZr11CKpWmBG/opK0PEcg3hCGyD0JTu3xCSgT6sE0jg/AkQ6p3Us",
|
||||
"upXPUpv+0D++fP5dDCpUpo6mCXvZb8ZpVhlvFrDIBS+Q8KLbFPO0pJ7oIDbYFq6knW0HDzKE5Fy81IzT",
|
||||
"uEfQRVkLUtBuRBuuvTG80yjoLWJ8DRwXGJMR+06F02Hd3eQywCcj76B3ODayVhUpqbSPhCVF++JDWkLV",
|
||||
"OfJxN326wqmIk+TMeQN9UnN1QUx946j2rPzIEhpTwbUh9M8y9RrtAujGtpG6ylxBYGzIovmefPvsj09F",
|
||||
"axrlfZ6TJOucIQ0C+wKdsp/w1oEOi0+TsRxEmIPzaTVWRHLxRc5/K0mtxiRyECkKJNcpb5trUrvd4V3X",
|
||||
"6GXw6dAY0UhTdxRWeCsuJV8AfJPYBU6Ur5v8enmSTQ+Y6fwdVmckR40adRedt/idp1IzubKPoyFXqcQv",
|
||||
"UuF4mvI785S1HtE9lbw2eWN76Evt4EM/F68CauscQM8HiRiD6dvuFpfWLXDqw22NoZp3A4zkaWZkiPwe",
|
||||
"sLnOr5Zy1mG3+thI83KWPIwcrmbb7NH7WY5RYw4JJ0MUMlcm9w/iPU5g5m1h7M18Vo1s9dUsnYTxdH6K",
|
||||
"xDxHRngW1V2ERPBxbyyg+V2833jIDDMnHDqR8+p8FLDpKV4CjcaklIFqLP1vv4PkLk4ug3J42xKTSlao",
|
||||
"7lzPxzJCNaP34wnAB+JJdoSfnsYJWRNszkCMeLg53wun8bJMHxQu3jMkN/37HGaqDOjf/oITIWlTmTD7",
|
||||
"23AJ1ez2GJoeX0sH3Drw8BGdYWWv6fPsyRvu6UM5PePn80/epScnwGW68AqpT4UXerD0NEurH29L68ST",
|
||||
"hMtPKVCcUq1/3e7NY8nsGBd3nBOJclDmr1h3UN4AFSXSMbq1YsldvnvHdfqh0Vmi1YllLKTakWapGhzm",
|
||||
"OQzAAa1fyL038kCAJzcMBvP1K3yjrPr5he8U0NZjYBJXv4uodVQsc5uc5GbYiXelDGKPWQn+qWpSRM/F",
|
||||
"OQ+dTScPs8OznIqpTRli98pWk7f9qy8eYcQwL4eKfeUkaks0Y09mN+tCAd3U7db6yMfTHwtoOqykF0vs",
|
||||
"1b1AC2gpTIs1ArpgjzzQoxIWjOLbBq7wH9+9e1MN5fJOJrYus3RPGOrexLV+ntTAnCLg0LxVNLevn+ih",
|
||||
"SABpN7Zt2JNm4J28Up4NwzEALDoCP6Fw3Jg3ypObMDdC+9/TvGZOJqFprh7fhTLgp63JP+7oSeK/48hr",
|
||||
"rJejTCzHD0hWQpl4H+eRZYdEAMRxPu4y+0qUztX7cs4S3PydPe03ak9hmJM3DzKjVlSxqYqKBT7QAgbO",
|
||||
"bEOAbMk+dCok0JbXP1HVJ9Zdye32yJO7A7JdSQ0W7hrl10qBmE/TyuxbqJNtrKw9id9xl8RSOnRrkcJL",
|
||||
"U1/YW8E2yONztWydDnfHb2yjlxSVFgtQbZWJOknU6HBUGzu0NXdiKSGBsFyGCIbnb17FyHCYtnI7rF9v",
|
||||
"5c1/JZPwf5z8x3+blT4pnUf9zc4kMqkIeyLQFGFY1P8gF9iuVsqHBd/UKQYz3uVdTXb6ONjuW/5mwX0o",
|
||||
"qMOOH86pLSqgkkdQvBtKOYp43m9xfV9A4Ilwq7/+u/4Q5jB5FGLTCtU6W+Wy8Kie3aXImb39jIzZ23vx",
|
||||
"Zf8ybFmhElMhN/LXXg+pJ19MVIf6erZiX4IF01/IVMBkF3arQxTdN9vQT/JSznzAGZMfNVkL9/5lEqwe",
|
||||
"In9nDt7/GkJ3JI0HEdbUeGjAjrK1jE4LbFHo8MqDTVd1Abs3yqkUknulAMWCcOrSn4D0XZrlyXvz4TCh",
|
||||
"m8xQzt5ED/Kk1JVhMlV7gsNnSdXOox1KixLg/wWIf5wq7NcJuDQ/mkqyLBCa+yew3JNJNqYkZa/6Ugbs",
|
||||
"LPF07v59FOOooyDIKQn6tBg16YBg2rTKDzugtktrrpXziHUcKeyDtSlFcj8Y91qZ2jo218Zh0L2cxMGM",
|
||||
"TB3lVJ2ixgc5banQKnon7ArFQH8MrjvDlqc4p2g/7PIQ4Jpi/zKIripxOW/399ZdnXfxzP+dmXY/Yc+q",
|
||||
"uj2qUeCg7K/lymsZB5KVYNPKs8+5V79B9AYWlmYcaThi3hqVXGgSWnHCiGh0ArEMq7g5bK6wziTDBUW1",
|
||||
"Cs5H5xVH1RJHmn3prrLKd4/F2WQjfPZygA9NWDlAuaDcRhvZ+C/gARqNOnEOEwwuO+kAUZamXui6KlUh",
|
||||
"pkIw3Ahp4t22c3br1Lh+DfVgKAdTfde5qKVJBNQ64wjU2RGF7Y4dkSiY9xw6nE4HLv3VO+76sVKCc/df",
|
||||
"LC14Wt+UVBZB+4Xj2RKun7yPP/dYIH6UZkWELVsGIBvu+Vicgut0tOP7SAB+9TBtzoSO+1d1cW6XVypw",
|
||||
"yAR5bGZek5GAR4Cg/Vb/k9otbQMaTGTGifUg/ThzGCRq3sRbIm0u3RBLZUC3vHXaoM43xPha24Zty0Ip",
|
||||
"nCzlIQC0gQccmBS9xmhMqtjEo6HBoNCUDTkwdfQqxQq06eXaNjXljkpHRP+z8/aOtEDd6uhPTutkL9Ub",
|
||||
"ojq1Et8+e/YsEQpoDpgcWp/ifJX0O2NFMnw4IFwENmAnid0ZljcR6we7+fBOhxr0b559M0a88xsdIrIB",
|
||||
"WDo03Dob7NI2/x4q8bKM05GcTyKz5ZX/JnUAWUm+TtMKVdSc/WeKfUdLI0fEVllGIzi60cE8deRaw8WR",
|
||||
"hHWRpCCP1pX0mYufZFBOKCw0Ceem5oTxmYoBe6stmKCIBZyQIvgE9ar5PaLGvV/Pr2DgAciZtIKvX7aP",
|
||||
"Mz1xrdlXf4/CIoabBAhDUjQXZdZeaPL1Bd8gbbh+RSDgdNIkBqIQXjCGBBVTBbi72JvEW0pttgHYtSh1",
|
||||
"Q+f82PPodIN46hYwL+ZyABF+Lr599i0ja9exxhqpkrB2Arvijr9tHzUrez7MlLEAz1bch68RtXJhYTq9",
|
||||
"X0ZpKP1Fx2Y0dqUN3bueisF3fH5FO8sf95WVcGUD1nT77oV1nO2ikyD8nN3a2Y43DMDSKBlcOCvrpfQh",
|
||||
"5lzYyjuwu6f8e9w63tqga5yM3k8geUzClE1kH5Pte+lZ4yHebdf5NbX6HIrUOBqLz4foUwmt4mJYIOOb",
|
||||
"JbXr/MaKEDh5D+8G7H6pBMn0VbOjBIl4J8mnUjIZBHqYJYJY2u3dx7D4k2Trc9Cs7xKVn8K+bGu6K+GR",
|
||||
"KBj+9yk0XlFbjO4oXJyld/Ml57JsTaSLBfTTPsUgwJ35jQhWfPPsWS8GIcOKqPki7OiNM8QTUnhQLhMd",
|
||||
"hFMwnh/MK1icBtAwvVyLTesxAEpcKFRhGLqHY723XgDUTey+94AWRYEbxUS6bY50B8gxMML+zF6fXiEy",
|
||||
"hbafr0zNiMRNmIgCBvgOmMmvRkn94cP/NwA=",
|
||||
"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",
|
||||
}
|
||||
|
||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||
|
||||
@@ -41,7 +41,6 @@ type Options struct {
|
||||
Hooks map[string][]HookMatcher
|
||||
MCPServers []*mcp.Server
|
||||
StdioMCP []mcp.Stdio
|
||||
Transcript io.Writer
|
||||
OnBadLine func(line []byte, reason string)
|
||||
}
|
||||
|
||||
@@ -197,9 +196,7 @@ type Session struct {
|
||||
events chan Event
|
||||
done chan struct{}
|
||||
|
||||
writeMu sync.Mutex
|
||||
transcript io.Writer
|
||||
scrub []byte
|
||||
writeMu sync.Mutex
|
||||
|
||||
pendingMu sync.Mutex
|
||||
pending map[string]chan controlResult
|
||||
@@ -235,15 +232,14 @@ type envelope struct {
|
||||
|
||||
func Start(ctx context.Context, opts Options) (*Session, error) {
|
||||
s := &Session{
|
||||
events: make(chan Event, 256),
|
||||
done: make(chan struct{}),
|
||||
transcript: opts.Transcript,
|
||||
pending: map[string]chan controlResult{},
|
||||
inflight: map[string]context.CancelFunc{},
|
||||
hooks: map[string]HookFunc{},
|
||||
servers: map[string]*mcp.Server{},
|
||||
id: initialID(opts),
|
||||
onBadLine: opts.OnBadLine,
|
||||
events: make(chan Event, 256),
|
||||
done: make(chan struct{}),
|
||||
pending: map[string]chan controlResult{},
|
||||
inflight: map[string]context.CancelFunc{},
|
||||
hooks: map[string]HookFunc{},
|
||||
servers: map[string]*mcp.Server{},
|
||||
id: initialID(opts),
|
||||
onBadLine: opts.OnBadLine,
|
||||
}
|
||||
s.lastLine.Store(time.Now().UnixNano())
|
||||
s.ctx, s.stop = context.WithCancel(context.Background())
|
||||
@@ -524,30 +520,10 @@ func (s *Session) write(v any) error {
|
||||
s.writeMu.Lock()
|
||||
defer s.writeMu.Unlock()
|
||||
|
||||
s.record("send", b)
|
||||
|
||||
_, err = s.stdin.Write(append(b, '\n'))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Session) record(dir string, line []byte) {
|
||||
if s.transcript == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(s.scrub) > 0 {
|
||||
line = bytes.ReplaceAll(line, s.scrub, []byte("[redacted]"))
|
||||
}
|
||||
|
||||
b, err := json.Marshal(map[string]any{"dir": dir, "msg": json.RawMessage(line)})
|
||||
if err != nil {
|
||||
slog.Error("record transcript", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.transcript.Write(append(b, '\n'))
|
||||
}
|
||||
|
||||
func (s *Session) readLoop(stdout io.Reader) {
|
||||
defer close(s.done)
|
||||
defer close(s.events)
|
||||
@@ -587,15 +563,6 @@ func (s *Session) handleLine(line []byte) {
|
||||
s.badLine(line, fmt.Sprintf("unrecognized type %q", env.Type))
|
||||
}
|
||||
|
||||
s.writeMu.Lock()
|
||||
if env.Type == "control_response" {
|
||||
s.learnScrub(env.Response)
|
||||
}
|
||||
if env.Type != "stream_event" {
|
||||
s.record("recv", line)
|
||||
}
|
||||
s.writeMu.Unlock()
|
||||
|
||||
switch env.Type {
|
||||
case "control_response":
|
||||
s.handleControlResponse(env.Response)
|
||||
@@ -638,21 +605,6 @@ func (s *Session) badLine(line []byte, reason string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) learnScrub(raw json.RawMessage) {
|
||||
resp := struct {
|
||||
Response struct {
|
||||
Account struct {
|
||||
Email string `json:"email"`
|
||||
} `json:"account"`
|
||||
} `json:"response"`
|
||||
}{}
|
||||
err := json.Unmarshal(raw, &resp)
|
||||
if err != nil || resp.Response.Account.Email == "" {
|
||||
return
|
||||
}
|
||||
s.scrub = []byte(resp.Response.Account.Email)
|
||||
}
|
||||
|
||||
func (s *Session) handleControlResponse(raw json.RawMessage) {
|
||||
resp := struct {
|
||||
Subtype string `json:"subtype"`
|
||||
|
||||
@@ -33,6 +33,8 @@ type Client struct {
|
||||
Login string
|
||||
Host string
|
||||
Commit string
|
||||
Build string
|
||||
Device string
|
||||
ConnectedAt time.Time
|
||||
LastActionAt *time.Time
|
||||
Sync *api.ClientSync
|
||||
@@ -69,7 +71,7 @@ func New(changed func()) *Registry {
|
||||
return &Registry{clients: map[string]*entry{}, transfers: map[string]Transfer{}, changed: changed}
|
||||
}
|
||||
|
||||
func (r *Registry) entry(id, login, host, commit string, now time.Time) *entry {
|
||||
func (r *Registry) entry(id, login, host, commit, build string, now time.Time) *entry {
|
||||
for k, e := range r.clients {
|
||||
if e.conn == nil && now.Sub(e.seen) > expireAfter {
|
||||
delete(r.clients, k)
|
||||
@@ -83,18 +85,19 @@ func (r *Registry) entry(id, login, host, commit string, now time.Time) *entry {
|
||||
e.Login = login
|
||||
e.Host = host
|
||||
e.Commit = commit
|
||||
e.Build = build
|
||||
e.seen = now
|
||||
return e
|
||||
}
|
||||
|
||||
func (r *Registry) Acted(id, login, host, commit string) bool {
|
||||
func (r *Registry) Acted(id, login, host, commit, build string) bool {
|
||||
if id == "" {
|
||||
return false
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
now := time.Now()
|
||||
e := r.entry(id, login, host, commit, now)
|
||||
e := r.entry(id, login, host, commit, build, now)
|
||||
e.LastActionAt = &now
|
||||
if now.Sub(e.notified) < time.Second {
|
||||
return false
|
||||
@@ -103,27 +106,28 @@ func (r *Registry) Acted(id, login, host, commit string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Registry) Synced(id, login, host, commit string, sync api.ClientSync) {
|
||||
func (r *Registry) Synced(id, login, host, commit, build string, sync api.ClientSync) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.entry(id, login, host, commit, time.Now()).Sync = &sync
|
||||
r.entry(id, login, host, commit, build, time.Now()).Sync = &sync
|
||||
}
|
||||
|
||||
func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, id, login, host, commit string) {
|
||||
func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, id, login, host, commit, build, device string) {
|
||||
ws.SetReadLimit(readLimit)
|
||||
c := &conn{ws: ws, done: make(chan struct{}), pending: map[string]chan api.ClientReply{}}
|
||||
|
||||
r.mu.Lock()
|
||||
now := time.Now().UTC()
|
||||
e := r.entry(id, login, host, commit, now)
|
||||
e := r.entry(id, login, host, commit, build, now)
|
||||
old := e.conn
|
||||
e.conn = c
|
||||
e.Device = device
|
||||
e.ConnectedAt = now
|
||||
r.mu.Unlock()
|
||||
if old != nil {
|
||||
old.ws.Close(websocket.StatusGoingAway, "replaced by a newer connection from this client")
|
||||
}
|
||||
slog.Info("client connected", "id", id, "host", host, "login", login, "commit", commit)
|
||||
slog.Info("client connected", "id", id, "host", host, "device", device, "login", login, "build", build, "commit", commit)
|
||||
r.changed()
|
||||
|
||||
defer func() {
|
||||
@@ -219,6 +223,16 @@ func (r *Registry) Recent() []Client {
|
||||
return rows
|
||||
}
|
||||
|
||||
func (r *Registry) RecentMacs() []Client {
|
||||
out := []Client{}
|
||||
for _, c := range r.Recent() {
|
||||
if c.Device == "Mac" {
|
||||
out = append(out, c)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Registry) Call(ctx context.Context, name string, req api.ClientRequest) (api.ClientReply, error) {
|
||||
r.mu.Lock()
|
||||
matches := []entry{}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -33,7 +32,6 @@ type Options struct {
|
||||
Reminder string
|
||||
MCPServers []*mcp.Server
|
||||
StdioMCP []mcp.Stdio
|
||||
Transcript io.Writer
|
||||
OnBadLine func(line []byte, reason string)
|
||||
}
|
||||
|
||||
@@ -88,9 +86,7 @@ type Session struct {
|
||||
events chan Event
|
||||
done chan struct{}
|
||||
|
||||
writeLock chan struct{}
|
||||
recordMu sync.Mutex
|
||||
transcript io.Writer
|
||||
writeLock chan struct{}
|
||||
|
||||
nextID atomic.Int64
|
||||
pendingMu sync.Mutex
|
||||
@@ -181,15 +177,14 @@ func spawn(opts Options) (*Session, error) {
|
||||
|
||||
func newSession(opts Options, stdin *os.File, stdout io.Reader) *Session {
|
||||
s := &Session{
|
||||
stdin: stdin,
|
||||
events: make(chan Event, 256),
|
||||
writeLock: make(chan struct{}, 1),
|
||||
done: make(chan struct{}),
|
||||
transcript: opts.Transcript,
|
||||
pending: map[int64]chan rpcResult{},
|
||||
servers: map[string]*mcp.Server{},
|
||||
reminder: opts.Reminder,
|
||||
onBadLine: opts.OnBadLine,
|
||||
stdin: stdin,
|
||||
events: make(chan Event, 256),
|
||||
writeLock: make(chan struct{}, 1),
|
||||
done: make(chan struct{}),
|
||||
pending: map[int64]chan rpcResult{},
|
||||
servers: map[string]*mcp.Server{},
|
||||
reminder: opts.Reminder,
|
||||
onBadLine: opts.OnBadLine,
|
||||
}
|
||||
for _, srv := range opts.MCPServers {
|
||||
s.servers[srv.Name] = srv
|
||||
@@ -552,8 +547,6 @@ func (s *Session) write(m message, deadline time.Time) error {
|
||||
return os.ErrDeadlineExceeded
|
||||
}
|
||||
|
||||
s.record("send", b)
|
||||
|
||||
err = s.stdin.SetWriteDeadline(deadline)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -566,26 +559,6 @@ func (s *Session) write(m message, deadline time.Time) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var emailField = regexp.MustCompile(`"email":"[^"]*"`)
|
||||
|
||||
func (s *Session) record(dir string, line []byte) {
|
||||
if s.transcript == nil {
|
||||
return
|
||||
}
|
||||
|
||||
line = emailField.ReplaceAll(line, []byte(`"email":"[redacted]"`))
|
||||
|
||||
b, err := json.Marshal(map[string]any{"dir": dir, "msg": json.RawMessage(line)})
|
||||
if err != nil {
|
||||
slog.Error("record transcript", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.recordMu.Lock()
|
||||
defer s.recordMu.Unlock()
|
||||
s.transcript.Write(append(b, '\n'))
|
||||
}
|
||||
|
||||
func (s *Session) readLoop(stdout io.Reader) {
|
||||
defer close(s.done)
|
||||
defer close(s.events)
|
||||
@@ -620,8 +593,6 @@ func (s *Session) handleLine(line []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
s.record("recv", line)
|
||||
|
||||
if m.Method == "" {
|
||||
s.handleResponse(line, m)
|
||||
return
|
||||
|
||||
@@ -35,7 +35,7 @@ func (s *Server) ConnectClient(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer ws.CloseNow()
|
||||
|
||||
s.clients.Serve(r.Context(), ws, id, actor(r), host, r.Header.Get("Byre-Commit"))
|
||||
s.clients.Serve(r.Context(), ws, id, actor(r), host, r.Header.Get("Byre-Commit"), r.Header.Get("Byre-Build"), r.Header.Get("Byre-Device"))
|
||||
}
|
||||
|
||||
func (s *Server) SetClientSync(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -58,7 +58,7 @@ func (s *Server) SetClientSync(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
slog.Warn("client icloud sync failing", "id", id, "host", host, "error", *sync.Error)
|
||||
}
|
||||
s.clients.Synced(id, actor(r), host, r.Header.Get("Byre-Commit"), sync)
|
||||
s.clients.Synced(id, actor(r), host, r.Header.Get("Byre-Commit"), r.Header.Get("Byre-Build"), sync)
|
||||
s.clientsChanged()
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (s *Server) ExecClient(w http.ResponseWriter, r *http.Request) {
|
||||
name = *req.Client
|
||||
}
|
||||
if name == "" {
|
||||
recent := s.clients.Recent()
|
||||
recent := s.clients.RecentMacs()
|
||||
if len(recent) == 0 {
|
||||
writeError(w, http.StatusServiceUnavailable, errors.New("no client Macs are connected"))
|
||||
return
|
||||
|
||||
@@ -200,7 +200,7 @@ func (s *Server) identify(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func (s *Server) acted(login string, r *http.Request) {
|
||||
if s.clients.Acted(r.Header.Get("Byre-Client"), login, r.Header.Get("Byre-Host"), r.Header.Get("Byre-Commit")) {
|
||||
if s.clients.Acted(r.Header.Get("Byre-Client"), login, r.Header.Get("Byre-Host"), r.Header.Get("Byre-Commit"), r.Header.Get("Byre-Build")) {
|
||||
s.clientsChanged()
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,9 @@ func (s *Server) connectedClients() []api.ConnectedClient {
|
||||
Id: c.ID,
|
||||
Login: c.Login,
|
||||
Host: c.Host,
|
||||
Device: c.Device,
|
||||
Commit: c.Commit,
|
||||
Build: c.Build,
|
||||
ConnectedAt: c.ConnectedAt,
|
||||
LastActionAt: c.LastActionAt,
|
||||
Sync: c.Sync,
|
||||
|
||||
@@ -129,42 +129,6 @@ func (s *Store) DeleteLeadInputs(ctx context.Context, ids []int64) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) AddLeadWake(ctx context.Context, owner, taskID string) error {
|
||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
active := false
|
||||
err := tx.QueryRowContext(ctx, `SELECT status = ? FROM tasks WHERE id = ?`, string(api.TaskStatusActive), taskID).Scan(&active)
|
||||
if errors.Is(err, sql.ErrNoRows) || (err == nil && !active) {
|
||||
return ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `INSERT OR IGNORE INTO lead_wakes (owner, task_id, created_at) VALUES (?, ?, ?)`, owner, taskID, now())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) TakeLeadWakes(ctx context.Context, taskID string) ([]string, error) {
|
||||
owners := []string{}
|
||||
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
rows, err := tx.QueryContext(ctx, `DELETE FROM lead_wakes WHERE task_id = ? RETURNING owner`, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
owner := ""
|
||||
err = rows.Scan(&owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
owners = append(owners, owner)
|
||||
}
|
||||
return rows.Err()
|
||||
})
|
||||
return owners, err
|
||||
}
|
||||
|
||||
func (s *Store) GetFocus(ctx context.Context, login string) (api.Focus, error) {
|
||||
f := api.Focus{}
|
||||
var task, project sql.NullString
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE lead_wakes;
|
||||
@@ -116,8 +116,10 @@ final class API: Sendable {
|
||||
self.origin = origin
|
||||
let config = URLSessionConfiguration.default
|
||||
config.httpAdditionalHeaders = [
|
||||
"Byre-Build": Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String,
|
||||
"Byre-Client": clientID,
|
||||
"Byre-Commit": Bundle.main.object(forInfoDictionaryKey: "ByreCommit") as! String,
|
||||
"Byre-Device": deviceKind,
|
||||
"Byre-Host": deviceName,
|
||||
]
|
||||
config.proxyConfigurations = [tailnetNode.proxy()]
|
||||
|
||||
@@ -84,6 +84,34 @@ struct OtherEntry: View {
|
||||
|
||||
var body: some View {
|
||||
let focused = focus.wrappedValue == otherStop
|
||||
HStack(alignment: .bottom, spacing: 8) {
|
||||
entry(focused)
|
||||
#if os(iOS)
|
||||
ComposerMenu(key: key, photos: true)
|
||||
.disabled(busy)
|
||||
#endif
|
||||
}
|
||||
.onAppear {
|
||||
text = store.drafts?.textAnswer(key) ?? ""
|
||||
}
|
||||
.onChange(of: text) {
|
||||
if text != store.drafts?.textAnswer(key) ?? "" {
|
||||
store.drafts?.setTextAnswer(key, text)
|
||||
}
|
||||
store.paneFocus.setDraft(key, !trimmed(text).isEmpty)
|
||||
}
|
||||
.onChange(of: store.drafts?.textAnswer(key)) { _, new in
|
||||
if (new ?? "") != text {
|
||||
text = new ?? ""
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
store.paneFocus.setDraft(key, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func entry(_ focused: Bool) -> some View {
|
||||
Group {
|
||||
if otherOpen(store, key) {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
@@ -135,23 +163,6 @@ struct OtherEntry: View {
|
||||
.onHover { hover = $0 }
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
text = store.drafts?.textAnswer(key) ?? ""
|
||||
}
|
||||
.onChange(of: text) {
|
||||
if text != store.drafts?.textAnswer(key) ?? "" {
|
||||
store.drafts?.setTextAnswer(key, text)
|
||||
}
|
||||
store.paneFocus.setDraft(key, !trimmed(text).isEmpty)
|
||||
}
|
||||
.onChange(of: store.drafts?.textAnswer(key)) { _, new in
|
||||
if (new ?? "") != text {
|
||||
text = new ?? ""
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
store.paneFocus.setDraft(key, false)
|
||||
}
|
||||
}
|
||||
|
||||
private func open() {
|
||||
|
||||
@@ -267,12 +267,12 @@ extension View {
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private struct ComposerMenu: View {
|
||||
struct ComposerMenu: View {
|
||||
let key: String
|
||||
let photos: Bool
|
||||
let models: [String]
|
||||
let selected: String
|
||||
let choose: @MainActor @Sendable (String) -> Void
|
||||
var models: [String] = []
|
||||
var selected = ""
|
||||
var choose: @MainActor @Sendable (String) -> Void = { _ in }
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var picking = false
|
||||
@@ -286,6 +286,11 @@ private struct ComposerMenu: View {
|
||||
} label: {
|
||||
Label("Attach photos", systemImage: "photo")
|
||||
}
|
||||
Button {
|
||||
paste()
|
||||
} label: {
|
||||
Label("Paste image", systemImage: "doc.on.clipboard")
|
||||
}
|
||||
}
|
||||
if !models.isEmpty {
|
||||
Picker("Model for the next turn", selection: Binding(get: { selected }, set: choose)) {
|
||||
@@ -316,6 +321,19 @@ private struct ComposerMenu: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func paste() {
|
||||
let images = UIPasteboard.general.images ?? []
|
||||
if images.isEmpty {
|
||||
overlays.showAlert("The clipboard has no image.")
|
||||
return
|
||||
}
|
||||
for image in images {
|
||||
if let data = image.jpegData(compressionQuality: 0.85) {
|
||||
store.images.add(key, data, name: nil, overlays)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ struct OverlayAlert: Identifiable {
|
||||
}
|
||||
|
||||
enum LeftTab {
|
||||
case lead, notes, workflows, status, server, settings
|
||||
case lead, status, notes, workflows, server, settings
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -51,6 +51,7 @@ final class Overlays {
|
||||
var switcher: SwitcherModel?
|
||||
var leftTab = LeftTab.lead
|
||||
var leadFocus = 0
|
||||
var projectFocus = false
|
||||
@ObservationIgnored var pickerFrame: CGRect = .zero
|
||||
@ObservationIgnored var menuHover = false
|
||||
var paneFrame: CGRect = .zero
|
||||
|
||||
@@ -439,7 +439,7 @@ private struct WebButton: View {
|
||||
.font(Fonts.font(13, weight: primary ? 700 : 400))
|
||||
.foregroundStyle(primary ? Palette.bg : danger ? Palette.danger : Palette.text)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.padding(.vertical, touchOnly ? 7 : 4)
|
||||
.background(primary ? fill : Palette.raised, in: RoundedRectangle(cornerRadius: 6))
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).stroke(primary ? fill : hover && enabled ? Palette.muted : Palette.border))
|
||||
.opacity(enabled ? (configuration.isPressed ? 0.8 : 1) : 0.45)
|
||||
@@ -458,7 +458,9 @@ struct SmallLinkButtonStyle: ButtonStyle {
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(color)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, touchOnly ? 3 : 0)
|
||||
.overlay(RoundedRectangle(cornerRadius: 4).stroke(border))
|
||||
.padding(.vertical, touchOnly ? 5 : 0)
|
||||
.opacity(enabled ? 1 : 0.45)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
@@ -124,11 +124,9 @@ private struct NoteRow: View {
|
||||
@ViewState private var frame: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 12) {
|
||||
Text(note.text)
|
||||
.textSelection(.enabled)
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
MarkdownView(blocks: parseMarkdown(note.text))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Button("Delete") {
|
||||
Task {
|
||||
if !(await overlays.showConfirm(["Delete this note? Your lead stops applying it.", note.text], action: "Delete", danger: true, anchor: frame)) {
|
||||
|
||||
@@ -278,6 +278,17 @@ final class Pages {
|
||||
}
|
||||
return png
|
||||
}
|
||||
#else
|
||||
func capture(_ view: WKWebView) async throws -> Data {
|
||||
guard view.window != nil, !view.isHidden else {
|
||||
throw ApiError(status: 0, message: "the tab isn't on screen: the human has the page view closed or another tab showing")
|
||||
}
|
||||
let image = try await view.takeSnapshot(configuration: nil)
|
||||
guard let png = image.pngData() else {
|
||||
throw ApiError(status: 0, message: "couldn't encode the capture as PNG")
|
||||
}
|
||||
return png
|
||||
}
|
||||
#endif
|
||||
|
||||
func close() {
|
||||
|
||||
@@ -113,15 +113,15 @@ private struct LeftPane: View {
|
||||
PaneTabButton(label: "Lead", active: tab == .lead) {
|
||||
pick(.lead)
|
||||
}
|
||||
PaneTabButton(label: "Status", active: tab == .status) {
|
||||
pick(.status)
|
||||
}
|
||||
PaneTabButton(label: "Notes", active: tab == .notes) {
|
||||
pick(.notes)
|
||||
}
|
||||
PaneTabButton(label: "Workflows", active: tab == .workflows) {
|
||||
pick(.workflows)
|
||||
}
|
||||
PaneTabButton(label: "Status", active: tab == .status) {
|
||||
pick(.status)
|
||||
}
|
||||
PaneTabButton(label: "Server", active: tab == .server) {
|
||||
pick(.server)
|
||||
}
|
||||
|
||||
@@ -28,14 +28,26 @@ private enum PhoneRoute: Hashable {
|
||||
case task
|
||||
}
|
||||
|
||||
private struct PhonePage: Equatable {
|
||||
var tab: PhoneTab
|
||||
var depth: Int
|
||||
var task: String?
|
||||
var taskTab: TaskTab?
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func taskTab(_ store: Store) -> TaskTab {
|
||||
store.sheet.flatMap { $0.owner == .lead ? nil : $0.tab } ?? store.primaryTab
|
||||
}
|
||||
|
||||
private enum MoreRoute: Hashable {
|
||||
case notes, workflows, status, settings
|
||||
case status, notes, workflows, settings
|
||||
}
|
||||
|
||||
private let moreTitles: [MoreRoute: String] = [
|
||||
.status: "Status",
|
||||
.notes: "Notes",
|
||||
.workflows: "Workflows",
|
||||
.status: "Status",
|
||||
.settings: "Settings",
|
||||
]
|
||||
|
||||
@@ -65,23 +77,9 @@ struct PhoneView: View {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
HStack(spacing: 4) {
|
||||
tabButton("Lead", .lead)
|
||||
HStack(spacing: 0) {
|
||||
tabButton("Tasks", .tasks)
|
||||
if !store.inbox.isEmpty {
|
||||
Badge(count: store.inbox.count)
|
||||
}
|
||||
}
|
||||
tabButton("More", .more)
|
||||
Spacer(minLength: 0)
|
||||
ConnectionMark()
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
tabBar
|
||||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.overlay {
|
||||
OverlayHost()
|
||||
}
|
||||
@@ -109,6 +107,9 @@ struct PhoneView: View {
|
||||
.onChange(of: tab == .tasks && !path.isEmpty, initial: true) { _, shown in
|
||||
store.paneFocus.setShown(shown)
|
||||
}
|
||||
.onChange(of: page) {
|
||||
dismissKeyboard()
|
||||
}
|
||||
.fullScreenCover(isPresented: Binding(get: { zoomed != nil }, set: { shown in
|
||||
if !shown {
|
||||
store.sheet = nil
|
||||
@@ -131,6 +132,51 @@ struct PhoneView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var tabBar: some View {
|
||||
HStack(spacing: 4) {
|
||||
tabButton("Lead", .lead)
|
||||
HStack(spacing: 0) {
|
||||
tabButton("Tasks", .tasks)
|
||||
if !store.inbox.isEmpty {
|
||||
Badge(count: store.inbox.count)
|
||||
}
|
||||
}
|
||||
tabButton("More", .more)
|
||||
Spacer(minLength: 0)
|
||||
ConnectionMark()
|
||||
Button {
|
||||
overlays.openSwitcher(store)
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
.font(Fonts.font(17, weight: 600))
|
||||
.foregroundStyle(Palette.accent)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("New task")
|
||||
Button {
|
||||
tab = .tasks
|
||||
path = []
|
||||
} label: {
|
||||
Image(systemName: "list.bullet")
|
||||
.font(Fonts.font(17, weight: 600))
|
||||
.foregroundStyle(Palette.accent)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("Task list")
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
}
|
||||
|
||||
private func shown(_ which: PhoneTab, @ViewBuilder _ content: () -> some View) -> some View {
|
||||
content()
|
||||
.opacity(tab == which ? 1 : 0)
|
||||
@@ -147,6 +193,13 @@ struct PhoneView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var page: PhonePage {
|
||||
if tab != .tasks || path.isEmpty {
|
||||
return PhonePage(tab: tab, depth: path.count)
|
||||
}
|
||||
return PhonePage(tab: tab, depth: path.count, task: store.selectedTaskId, taskTab: taskTab(store))
|
||||
}
|
||||
|
||||
private var zoomed: SheetView? {
|
||||
switch store.sheet?.view {
|
||||
case .image, .diagram: store.sheet?.view
|
||||
@@ -199,8 +252,6 @@ private struct ConnectionMark: View {
|
||||
Circle()
|
||||
.fill(store.connected ? Palette.ok : Palette.muted)
|
||||
.frame(width: 8, height: 8)
|
||||
Text(store.me?.displayName ?? "")
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
.lineLimit(1)
|
||||
}
|
||||
@@ -280,7 +331,7 @@ private struct PhoneTask: View {
|
||||
var body: some View {
|
||||
let selected = store.selectedTaskId.flatMap { store.findTask($0) }
|
||||
let accent = selected.map { taskPalette[$0.color] } ?? Palette.accent
|
||||
let tab = store.sheet.flatMap { $0.owner == .lead ? nil : $0.tab } ?? store.primaryTab
|
||||
let tab = taskTab(store)
|
||||
VStack(spacing: 0) {
|
||||
TaskBar()
|
||||
.overlay(alignment: .leading) {
|
||||
@@ -311,15 +362,21 @@ private struct PhoneTask: View {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func dismissKeyboard() {
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
|
||||
private struct MoreScreen: View {
|
||||
@ViewState private var route = MoreRoute.notes
|
||||
@ViewState private var route = MoreRoute.status
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 4) {
|
||||
ForEach([MoreRoute.notes, .workflows, .status, .settings], id: \.self) { r in
|
||||
ForEach([MoreRoute.status, .notes, .workflows, .settings], id: \.self) { r in
|
||||
PaneTabButton(label: moreTitles[r]!, active: route == r) {
|
||||
route = r
|
||||
dismissKeyboard()
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
|
||||
@@ -8,8 +8,10 @@ private let refreshKinds: Set<String> = ["task.merged", "project.created"]
|
||||
|
||||
struct ProjectView: View {
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var todos: [String: Result<Todo, ApiError>] = [:]
|
||||
@ViewState private var off: (() -> Void)?
|
||||
@FocusState private var focused: Bool
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@@ -23,6 +25,17 @@ struct ProjectView: View {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.focusable()
|
||||
.focusEffectDisabled()
|
||||
.focused($focused)
|
||||
.onChange(of: overlays.projectFocus, initial: true) {
|
||||
if overlays.projectFocus {
|
||||
overlays.projectFocus = false
|
||||
DispatchQueue.main.async {
|
||||
focused = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
off = store.events.add { ev in
|
||||
if refreshKinds.contains(ev.kind), let projectId = ev.projectId {
|
||||
|
||||
@@ -7,12 +7,13 @@ typealias ClientRequest = Components.Schemas.ClientRequest
|
||||
typealias ClientReply = Components.Schemas.ClientReply
|
||||
typealias ClientSync = Components.Schemas.ClientSync
|
||||
|
||||
#if os(macOS)
|
||||
private let retryAfter = Duration.seconds(2)
|
||||
private let checkEvery = Duration.seconds(10)
|
||||
private let staleAfter: TimeInterval = 25
|
||||
private let maxOutput = 64 << 10
|
||||
private let maxMessage = 256 << 20
|
||||
|
||||
#if os(macOS)
|
||||
private let maxOutput = 64 << 10
|
||||
private let maxRead = 64 << 20
|
||||
private let copyTimeoutMs = 10 * 60 * 1000
|
||||
private let hands = Hands()
|
||||
@@ -27,6 +28,7 @@ private let signScript = """
|
||||
esac
|
||||
exec "$program" --status-fd=2 -bsau "$(git config user.signingkey || echo "$1")" >"$2"
|
||||
"""
|
||||
#endif
|
||||
|
||||
@MainActor
|
||||
final class Remote {
|
||||
@@ -157,6 +159,10 @@ final class Remote {
|
||||
|
||||
private func perform(_ req: ClientRequest) async throws -> ClientReply {
|
||||
switch req.op {
|
||||
#if os(iOS)
|
||||
case .bash, .exec, .upload, .download, .readFile, .writeFile, .hands, .sign:
|
||||
throw ApiError(status: 0, message: "an iPhone only shows and drives pages and files in byre's app (show_page, show_file, drive_page)")
|
||||
#else
|
||||
case .bash:
|
||||
let home = FileManager.default.homeDirectoryForCurrentUser.path
|
||||
let (code, output) = try await spawn(["/bin/zsh", "-l", "-c", req.command ?? ""], in: home, timeoutMs: req.timeoutMs ?? 120_000)
|
||||
@@ -190,6 +196,7 @@ final class Remote {
|
||||
case .sign:
|
||||
let (code, status, signature) = try await sign(Data(req.data?.data ?? []), as: req.ident ?? "", timeoutMs: req.timeoutMs ?? 120_000)
|
||||
return ClientReply(id: req.id, exitCode: code, output: status, data: Base64EncodedData(signature))
|
||||
#endif
|
||||
case .openPage:
|
||||
if let address = req.url {
|
||||
guard let url = URL(string: address) else {
|
||||
@@ -219,6 +226,7 @@ final class Remote {
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private func sign(_ payload: Data, as ident: String, timeoutMs: Int) async throws -> (Int, String, Data) {
|
||||
let fm = FileManager.default
|
||||
let dir = fm.temporaryDirectory.appending(path: "byre-sign-\(UUID().uuidString)", directoryHint: .isDirectory)
|
||||
@@ -293,8 +301,10 @@ final class Remote {
|
||||
}
|
||||
try fm.moveItem(at: entries[0], to: target)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private func expand(_ path: String) -> URL {
|
||||
URL(filePath: (path as NSString).expandingTildeInPath)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ struct ChecksView: View {
|
||||
case "required":
|
||||
return "Required"
|
||||
case "agent":
|
||||
return "One of claude, codex and agy required"
|
||||
return "One of claude, codex or agy required"
|
||||
default:
|
||||
return "Optional"
|
||||
}
|
||||
|
||||
@@ -254,13 +254,7 @@ final class Connection {
|
||||
store.startGlobalStream(after: status.lastEventSeq) { [weak self] in
|
||||
self?.reconnected(status.startedAt)
|
||||
}
|
||||
#if os(macOS)
|
||||
store.remote.start()
|
||||
#else
|
||||
if let sync = Servers.shared.sync {
|
||||
store.reportSync(sync)
|
||||
}
|
||||
#endif
|
||||
forwards.sync(store.api, Set(store.activeTasks().map(\.task.id)))
|
||||
Notifier.shared.deliver()
|
||||
return true
|
||||
|
||||
@@ -3,6 +3,7 @@ import SwiftUI
|
||||
|
||||
typealias AgentProcess = Components.Schemas.AgentProcess
|
||||
typealias Zombie = Components.Schemas.Zombie
|
||||
typealias ConnectedClient = Components.Schemas.ConnectedClient
|
||||
typealias VendorUsage = Components.Schemas.Usage
|
||||
typealias UsageWindow = Components.Schemas.UsageWindow
|
||||
|
||||
@@ -440,28 +441,66 @@ private struct ClientTable: View {
|
||||
if status.clients.isEmpty {
|
||||
Text("No clients connected.").foregroundStyle(Palette.muted)
|
||||
} else {
|
||||
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 2) {
|
||||
GridRow {
|
||||
ForEach(["Mac", "User", "Build", "Connected", "Last action", "iCloud"], id: \.self) { h in
|
||||
Text(h).font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
}
|
||||
}
|
||||
Divider().overlay(Palette.border)
|
||||
ForEach(Array(status.clients.enumerated()), id: \.offset) { _, c in
|
||||
GridRow {
|
||||
Text(c.host.isEmpty ? "—" : c.host)
|
||||
Text(c.login)
|
||||
Text(c.commit.isEmpty ? "—" : String(c.commit.prefix(8))).help(c.commit)
|
||||
Text(since(c.connectedAt))
|
||||
Text(c.lastActionAt.map(since) ?? "—")
|
||||
SyncCell(sync: c.sync)
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
Divider().overlay(Palette.border)
|
||||
}
|
||||
ViewThatFits(in: .horizontal) {
|
||||
grid
|
||||
stacked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var grid: some View {
|
||||
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 2) {
|
||||
GridRow {
|
||||
ForEach(["Name", "Device", "User", "Build", "Connected", "Last action", "iCloud"], id: \.self) { h in
|
||||
Text(h).font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
}
|
||||
}
|
||||
Divider().overlay(Palette.border)
|
||||
ForEach(Array(status.clients.enumerated()), id: \.offset) { _, c in
|
||||
GridRow {
|
||||
Text(c.host.isEmpty ? "—" : c.host).fixedSize()
|
||||
Text(c.device.isEmpty ? "—" : c.device).fixedSize()
|
||||
Text(c.login).fixedSize()
|
||||
Text(build(c)).help(c.commit).fixedSize()
|
||||
Text(ago(c.connectedAt)).fixedSize()
|
||||
Text(c.lastActionAt.map(since) ?? "—").fixedSize()
|
||||
SyncCell(sync: c.sync).fixedSize()
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
Divider().overlay(Palette.border)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var stacked: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Divider().overlay(Palette.border)
|
||||
ForEach(Array(status.clients.enumerated()), id: \.offset) { _, c in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 8) {
|
||||
Text(c.host.isEmpty ? "—" : c.host)
|
||||
Text(c.device.isEmpty ? "—" : c.device).font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
Spacer(minLength: 0)
|
||||
Text("iCloud").font(Fonts.font(11)).foregroundStyle(Palette.muted)
|
||||
SyncCell(sync: c.sync)
|
||||
}
|
||||
Text("\(c.login) · build \(build(c))")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
Text("Connected \(ago(c.connectedAt)) ·last action \(c.lastActionAt.map(since) ?? "—")")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
.padding(.vertical, 6)
|
||||
Divider().overlay(Palette.border)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func build(_ c: ConnectedClient) -> String {
|
||||
c.build.isEmpty ? "—" : c.build
|
||||
}
|
||||
}
|
||||
|
||||
private struct SyncCell: View {
|
||||
|
||||
@@ -89,9 +89,7 @@ final class Store {
|
||||
let imageCache = ImageCache()
|
||||
let events = Listeners<ByreEvent>()
|
||||
let paneFocus: PaneFocus
|
||||
#if os(macOS)
|
||||
let remote: Remote
|
||||
#endif
|
||||
let forwards: Forwards
|
||||
let pages: Pages
|
||||
|
||||
@@ -115,10 +113,8 @@ final class Store {
|
||||
self.pages = pages
|
||||
leadFeed = Feed(shown: { true })
|
||||
paneFocus = PaneFocus(api: api)
|
||||
#if os(macOS)
|
||||
remote = Remote(api: api)
|
||||
remote.store = self
|
||||
#endif
|
||||
pages.popup = { [weak self] page in
|
||||
self?.openPage(page)
|
||||
}
|
||||
@@ -144,9 +140,7 @@ final class Store {
|
||||
stopped = true
|
||||
globalStream?.stop()
|
||||
leadStream?.stop()
|
||||
#if os(macOS)
|
||||
remote.stop()
|
||||
#endif
|
||||
drafts?.flush()
|
||||
await paneFocus.leave()
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ func runGo(_ key: String, _ store: Store, _ overlays: Overlays) {
|
||||
overlays.switcher?.close()
|
||||
store.sheet = nil
|
||||
if let project = store.focusedProject() ?? store.projects.first {
|
||||
overlays.projectFocus = true
|
||||
store.focusProject(project.id)
|
||||
}
|
||||
case "l":
|
||||
@@ -279,6 +280,7 @@ final class SwitcherModel {
|
||||
go(task.id)
|
||||
case .project(let p):
|
||||
close()
|
||||
overlays.projectFocus = true
|
||||
store.focusProject(p.id)
|
||||
case .newTask(let p):
|
||||
create(p.id, .init())
|
||||
@@ -301,7 +303,9 @@ final class SwitcherModel {
|
||||
if store.inbox.contains(where: { $0.taskId == taskId }) {
|
||||
store.showQuestion(taskId)
|
||||
} else {
|
||||
store.composerFocus = taskId
|
||||
if !touchOnly {
|
||||
store.composerFocus = taskId
|
||||
}
|
||||
store.focusTask(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import Network
|
||||
import Observation
|
||||
import SwiftUI
|
||||
import TailscaleKit
|
||||
#if os(iOS)
|
||||
import SafariServices
|
||||
#endif
|
||||
|
||||
func slug(_ text: String) -> String {
|
||||
var out = ""
|
||||
@@ -288,6 +291,7 @@ private struct NodeLoginRow: View {
|
||||
let role: String
|
||||
let state: String
|
||||
let authURL: URL?
|
||||
@State private var signingIn = false
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 12) {
|
||||
@@ -307,9 +311,13 @@ private struct NodeLoginRow: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
if state != "Running" {
|
||||
Button("Sign in") {
|
||||
#if os(macOS)
|
||||
if let authURL {
|
||||
openExternal(authURL)
|
||||
}
|
||||
#else
|
||||
signingIn = true
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(WebButtonStyle(primary: true))
|
||||
.disabled(authURL == nil)
|
||||
@@ -319,5 +327,30 @@ private struct NodeLoginRow: View {
|
||||
.padding(12)
|
||||
.background(Palette.bg, in: RoundedRectangle(cornerRadius: 8))
|
||||
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Palette.border))
|
||||
#if os(iOS)
|
||||
.sheet(isPresented: $signingIn) {
|
||||
if let authURL {
|
||||
SafariView(url: authURL)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
.onChange(of: state) {
|
||||
if state == "Running" {
|
||||
signingIn = false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private struct SafariView: UIViewControllerRepresentable {
|
||||
let url: URL
|
||||
|
||||
func makeUIViewController(context: Context) -> SFSafariViewController {
|
||||
SFSafariViewController(url: url)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ controller: SFSafariViewController, context: Context) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1054,7 +1054,11 @@ private func toolTarget(_ input: (any Sendable)?, _ root: String) -> String {
|
||||
guard let path = (i?["file_path"] ?? i?["path"]) as? String else {
|
||||
return ""
|
||||
}
|
||||
return relative(path, root)
|
||||
let short = relative(path, root)
|
||||
if short != path || !path.hasPrefix("/") {
|
||||
return short
|
||||
}
|
||||
return URL(fileURLWithPath: path).lastPathComponent
|
||||
}
|
||||
|
||||
private func relative(_ path: String, _ root: String) -> String {
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ ditto "$build/TailscaleKit.framework" "$app/Contents/Frameworks/TailscaleKit.fra
|
||||
cp "$mac/Info.plist" "$app/Contents/Info.plist"
|
||||
plutil -insert ByreCommit -string "$(git -C "$root" rev-parse HEAD)" "$app/Contents/Info.plist"
|
||||
plutil -insert CFBundleShortVersionString -string "$version" "$app/Contents/Info.plist"
|
||||
plutil -insert CFBundleVersion -string "$(git -C "$root" rev-list --count HEAD)" "$app/Contents/Info.plist"
|
||||
plutil -insert CFBundleVersion -string "$(git -C "$root" log -1 --format=%ct)" "$app/Contents/Info.plist"
|
||||
cp "$mac"/fonts/*.woff2 "$app/Contents/Resources/fonts/"
|
||||
cp "$icns" "$app/Contents/Resources/byre.icns"
|
||||
cp "$mac/byre.provisionprofile" "$app/Contents/embedded.provisionprofile"
|
||||
|
||||
@@ -8,4 +8,5 @@ ditto "$mount/Byre.app" /Applications/Byre.app.new
|
||||
hdiutil detach "$mount"
|
||||
rm -rf /Applications/Byre.app
|
||||
mv /Applications/Byre.app.new /Applications/Byre.app
|
||||
plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist
|
||||
plutil -extract ByreCommit raw /Applications/Byre.app/Contents/Info.plist
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ plutil -insert CFBundleIcons -xml "$(plutil -extract CFBundleIcons xml1 -o - "$b
|
||||
plutil -insert ByreCommit -string "$(git -C "$root" rev-parse HEAD)" "$plist"
|
||||
version=$(git -C "$root" describe --tags --abbrev=0)
|
||||
plutil -insert CFBundleShortVersionString -string "${version#v}" "$plist"
|
||||
plutil -insert CFBundleVersion -string "$(date -u +%Y%m%d.%H%M%S)" "$plist"
|
||||
plutil -insert CFBundleVersion -string "$(git -C "$root" log -1 --format=%ct)" "$plist"
|
||||
platform=$(xcrun --sdk "$sdk" --show-sdk-platform-version)
|
||||
sdkbuild=$(xcrun --sdk "$sdk" --show-sdk-build-version)
|
||||
plutil -insert CFBundleSupportedPlatforms -xml "<array><string>$(if [ "$sdk" = iphoneos ]; then echo iPhoneOS; else echo iPhoneSimulator; fi)</string></array>" "$plist"
|
||||
|
||||
Reference in New Issue
Block a user