A Service Manager Born from a Rant: One Afternoon, 49M Tokens, and a Tauri Desktop App That Ships
A few days ago, it happened again: starting a new project, port was occupied. Checked with lsof, and there sat a java process with a long jar name that I couldn't identify which project it belonged to. Kill it? Afraid it might be a service still in use. Don't kill it? My project couldn't start. Went back and forth for ten minutes, finally discovered it was an old project from debugging half a month ago that I'd forgotten to shut down.
As a full-stack developer, I have not only company projects but also plenty of my own: a Vue admin dashboard, Spring Boot APIs, Go utilities, services started by Python scripts — all piled onto one Mac. I can't even tell which ones are running and which ports they're occupying. And with many Node versions managed via nvm, sometimes I don't know which version a project uses until a startup error reveals it.
So I'd wanted to build this management tool for a long time. The idea is simple: scan, list, one-click start/stop, and view logs. But actually writing it myself was still somewhat difficult. I'd thought about using Claude before but kept forgetting. This time I happened to subscribe to Volcano Engine's Agent Plan, which connects directly to Claude Code and lets you configure doubao-seed-evolving. I looked up this model and found one setting particularly appealing: the ID carries no version number; the official team iterates weekly, and the subscriber's configuration doesn't need a single change. This upgrade brings 1M context and a 256k output limit.
First, check out the effect:
Setup, about ten minutes, two things to watch
The subscription process isn't complicated, but two spots are easy to trip on. Let me be clear.
Subscribe to a plan
Find Agent Plan in the console, subscribe as needed. Entry here: Subscription address. Details on each tier's benefits are in the official docs: Plan details. I bought the Medium tier, 49.9 per month, with a quota of 5 hours 10000 AFP, 35000 weekly, 100000 monthly — enough for personal development.
There are many model types available, including the Seed series and mainstream domestic models like GLM, Minimax, DeepSeek, and Kimi.
Watch point one: The Key is not that Key
The plan has a dedicated API Key, different from the usual pay-as-you-go key. Using the wrong one still charges you by usage, wasting the subscription. I almost configured it with the old key.
Watch point two: Base URL is also separate
Two protocols, two addresses. Anthropic protocol uses https://ark.cn-beijing.volces.com/api/plan, OpenAI protocol uses /api/plan/v3. If you slip and configure /api/v3, congratulations, you're back on pay-as-you-go.
The docs list connection methods for tools like Claude Code, Codex, OpenCode, and TRAE — just copy them (but make sure to select the plan's key).
After configuration, switch with /model doubao-seed-evolving and get started.
Opening with a rant
There was no requirements document. I typed my rant verbatim, essentially the opening paragraph: a pile of Vue, Java, Go, Python services, forgotten which ones are running, version mismatches, script-started ones nowhere to be found — build me something to manage it all.
After sending it, it pondered for about a minute. I thought it would just start writing, but it didn't. Instead, it asked me: do you want something light or heavy? A menu bar utility, a full desktop manager — three options, each with pros and cons listed for me to choose.
I wanted the full version. It recommended Tauri + Vue3 as the tech stack, citing small bundle size and low memory usage, suitable for a background resident. I already write Vue, so no objections.
Then it broke down the features into seven items and confirmed each with me: auto-detect project type, read version numbers, one-click start/stop, reverse-scan running services, log collection, SQLite for config storage, dark interface. I give this step high marks — without confirmation, it easily goes off on its own and builds things you don't want.
Setting rules, then letting it spin on its own
Before starting, I did two things.
First, I fed it my coding standards: no large classes, no large files, reference my own project skills, split Rust modules by responsibility. Second, I turned on /loop to let it iterate on its own — finish one round, check itself, then continue, without me feeding it line by line.
It laid out a nine-step plan and started running. I genuinely slacked off in between, occasionally glancing at the terminal.
Optimization, three rounds
It wasn't a one-shot deal, honestly.
White screen
It said the frontend build was done. I opened it — black screen. I pressed it with "Are you sure the frontend is fine?" It went back to recheck and found that MainLayout.vue used the h function without importing it from Vue, and a store was called outside setup. After fixing, it created an eight-item checklist and ticked each one off. This self-checking behavior surprised me more than the bug fix itself.
Clicking did nothing
The first version of the interface appeared. Clicked "Scan Directory" — nothing happened. No error, no loading, as if it were dead. I described the symptom, and it added loading states and success/failure prompts, conveniently replacing a fragile component.
Overlapping modals
The third round was styling: modals and lists overlapping, spacing a mess. This kind of "looks wrong" it genuinely can't detect — it can't see the screen. I described what was off, and it fixed it. After one round, hover states, spacing, and left-right alignment were all smooth.
So logic-layer errors it can handle on its own; UI oddities need human eyes. This step can't be skipped.
Hands-on testing
Started around 4-5 PM, by 8 PM I was testing with real projects.
Scan directory first
In settings, added the path of a Vue project to the scan directory, clicked scan. It recognized it: Vue, Node 22.12.0, port 18100, start command npm run dev — all correct, no guessing.
Then reverse scan
I had another Java archive service, previously started in the terminal, not in any scan directory at all. Clicked "Scan Running Services," and it scanned all listening port processes system-wide, reverse-looked up working directories. electronic-archives was caught just like that, port 18881, check it to include in the list. This is exactly the effect I wanted most — terminal-started, IDE-started, none can hide.
Start/Stop
Tried starting and stopping several rounds. Response was quick — status changed immediately after clicking, no waiting for the process to slowly exit. Port numbers are clickable; clicking one opens the browser directly. This is handy.
Logs
Services started through it can view logs directly. Opened the web project's logs — Vite's output was all there as-is, ready in 259 ms, no different from running in the terminal, plus timestamps.
Settings, removal, and other corners
The settings page can configure multiple scan directories. Switches for auto-start on boot and auto-restart on crash are there. NVM, terminal, and editor paths are also customizable.
Removing a project from the list triggers a secondary confirmation dialog, and it specifically added the line "will not delete local files." It added this on its own; I didn't mention it. That's very user-friendly.
Glanced at the code
A working interface isn't enough; I plan to use this long-term, so I had to review the code.
The structure is well-separated: the commands layer only forwards parameters, detectors are one file per language, managers handle processes, database, and nvm separately. To add recognition for a new language later, just drop a new file into detectors — no need to touch anything else.
Noted a few things while browsing. Reading the Node version checks .nvmrc, .node-version, and package.json's engines in descending priority — exactly my manual troubleshooting habit. The start command guesses among dev, serve, start in scripts — a priority I never taught it. Before starting a process, it stuffs the bin directories of all Node versions under nvm into PATH — crude, but the "npm not found" problem just disappears. Stopping a process sends TERM first, waits two seconds, then force-kills — no orphan processes left behind. "Open project directory" didn't wrestle with Tauri's shell plugin permissions; it directly calls open on Mac and explorer on Windows. This workaround is very old-school developer; I chuckled when I saw it.
I won't paste the specific implementation into the article. Once I clean up the rough edges, the code will be open-sourced.
Settling accounts
At the end, I sent "scan our project and see how many lines of code."
Rust backend: 2004 lines. Vue/TS frontend: 1343 lines. Total: 3347. The breakdown also matches the standards: process management 492, database 298, language detectors 301, nvm management 173, IPC layer 242. No two-thousand-line mega-files grew. It also casually reported the packaged installer is about 5MB — I haven't verified this number myself yet, just noting it.
Then I asked it to summarize, verbatim: "I hope you summarize from the beginning of our conversation, mainly summarizing what I asked you to do and the optimization matters." In 14 seconds, it sorted through the entire process: seven requirements, every modification I raised in each round, how the architecture was split — all matching my memory. After an afternoon of back-and-forth, it still remembered that opening rant. The 1M context wasn't wasted.
Let's also show the token bill. For doubao-seed-evolving, writing this project on July 21st consumed a total of over 49 million tokens. I think the effect was very good — at the very least, it solved my needs and pain points.
Finally
This tool now resides permanently on my Mac. Which service is running? Open it, glance, and you know — no more checking lsof one by one.
Looking back at the cost, from ranting in the afternoon to testing with real projects in the evening — just one afternoon. Most of the time, it spun in its own loop while I did my own thing. Tokens burned: over 49 million — a scary number, but the plan's 5-hour usage warning never popped once. The 49.9 ticket was more than enough.
For me, the most practical thing is that this isn't a demo built for a screenshot and then discarded. These past couple of days, I've been starting and stopping my current projects right inside it. A tool I'd been thinking about for over half a year just landed like that.
Next, I plan to have it go through Windows adaptation on a real machine. Once those small code issues are cleaned up, it'll be open-sourced. Anyway, the model iterates weekly, and the ID doesn't even need changing. Next time I work, let's see how much it has improved.