AI 资讯
Shipping a Livewire 4 + Flux admin UI inside a package: four gotchas that 500'd on me
Bundling an admin UI inside a Laravel package is a different game from building one in an app. The app's conveniences — a compiled Vite manifest, a registered layout, your own Livewire components — aren't there. Today, getting the bundled admin UI in laravel-config-webhook to actually render meant walking through four separate 500s. Each one is a small, sharp lesson about the boundary between a package and its host app. 1. A Livewire 4 component name can't contain :: I registered the component with a namespaced-looking name and got a ComponentNotFoundException at runtime. The cause is subtle: under Livewire 4, a name containing :: triggers namespace resolution that ignores singly-registered components. So a "nice looking" name silently routes to a lookup that will never find it. The fix is to register a plain, dotted name: // ❌ looks tidy, but the "::" sends Livewire down a namespace path Livewire :: component ( 'config-webhook::webhooks' , Webhooks :: class ); // ✅ a flat dotted name resolves to the singly-registered component Livewire :: component ( 'config-webhook.webhooks' , Webhooks :: class ); Lesson: in a package, treat the component name as an identifier with framework-reserved characters — :: is not yours to use. 2. Flux ships Heroicons, not Pro/Lucide names The free tier of Flux ships Heroicons . Reach for a Pro-only or Lucide-style name and it throws at runtime. I'd used webhook , ellipsis , and list ; the free equivalents are bolt , ellipsis-horizontal , and queue-list . This is the same trap that bit my SSO package — which is exactly why I now guard it with a static test that reads the Blade and checks every icon against Flux's actual stub files. (Separate post on that.) If you ship a package UI with Flux, assume free-tier icons only unless you require Pro. 3. Don't @vite host assets that don't exist The bundled fallback layout @vite -d the host app's assets. In a fresh consumer (or the package's own workbench) there's no compiled manifest, so you get a
AI 资讯
A test that catches the bug your feature tests can't see
There's a class of bug that's maddening: it passes every test you have, then crashes in the user's face. I hit one in the admin UI of laravel-config-sso today, and the real fix wasn't changing an icon name — it was writing a test that could see the bug in the first place. The bug: wrong icon name, crashes only at runtime The admin UI uses Flux . Flux resolves icons through <flux:delegate-component> , and it throws for a name that doesn't exist: Flux component [icon.ellipsis] does not exist. It's an easy mistake. Flux ships Heroicons , not Lucide. So your Lucide reflexes lie to you: You type (Lucide) Flux wants (Heroicon) ellipsis ellipsis-horizontal trash-2 trash eye-off eye-slash Why feature tests don't catch it Here's the interesting part. I had a feature test that hits the admin route and asserts 200. Green. But the real UI crashes. How? Because in the headless test harness, Flux renders icons as no-ops. No real <flux:delegate-component> boots, so the icon name never gets resolved. The crash only surfaces under a full boot ( testbench serve ) — exactly where your automated tests don't go. Analogy: it's like a spell-checker that only runs when you print the document, not while you type. Your tests type away happily. The crash waits at the printer. The fix: a static test that reads the Blade and validates every icon Instead of relying on runtime, I wrote a Pest test that reads the Blade view, extracts every icon name (static and inside dynamic expressions), and asserts Flux actually ships a stub for each one: $fluxIconStubs = base_path ( 'vendor/livewire/flux/stubs/resources/views/flux/icon' ); it ( 'only references Flux icons that exist' , function () use ( $fluxIconStubs ) { expect ( is_dir ( $fluxIconStubs )) -> toBeTrue ( "Flux icon stubs not found" ); $view = file_get_contents ( __DIR__ . '/../../resources/views/livewire/sso-providers.blade.php' ); // Static `icon="name"` plus quoted tokens inside dynamic // `icon="{{ $cond ? 'eye-slash' : 'eye' }}"` expressio
AI 资讯
Making encrypted Laravel config backups portable across APP_KEYs
Here's a fun one. You build a package that backs up an app's config — the .env plus the settings stored encrypted in the database — into a single password-protected ZIP. The whole selling point is portability : take a backup on server A, restore it on server B, even when the two servers have different APP_KEY s. Then you write a test that actually changes the key during a restore, and it fails. The DB settings come back garbled. Turns out the bug wasn't in the encryption at all. It was in a cache I forgot was there. Today I shipped 1.1.0 of laravel-config-backup and this portability fix was the headline. Let me walk through it, because the lesson generalizes way beyond this package. Why APP_KEY portability is even a thing Laravel encrypts things with APP_KEY . Encrypted Eloquent casts, signed cookies, sessions — all of it keys off that value. So if you naively mysqldump a table with encrypted columns and load it onto another server, every encrypted column is now ciphertext that the new key can't decrypt. Dead data. The trick this package uses is to store the archive contents decrypted . When I export the database, rows go out through their casts , so an encrypted column becomes a plain value inside the ZIP (the ZIP itself is AES-256 password-encrypted, so it's not sitting around in plaintext). On import, each row is written back through the model , which means the cast re-encrypts it with whatever APP_KEY is active on the destination. Server A (key A) Archive (decrypted) Server B (key B) ──────────────── ─────────────────── ──────────────── settings.payload ──decrypt──▶ "Portable" ──import──▶ settings.payload (ciphertext A) (cast) (cast) (ciphertext B) Think of it like shipping furniture: you don't ship the assembled wardrobe through a doorway it doesn't fit, you flat-pack it and reassemble at the destination with the screws you have there. The restore sequence A restore that also brings a new .env has to be careful about ordering . Here's the real flow: public func
AI 资讯
AI Observability: Logs, Prompts, Tool Calls, And Cost
Here's a five-line function. It calls an LLM, logs the answer, returns it. async function ask ( question : string ) { const res = await openai . responses . create ({ model : " o4-mini " , input : question }); console . log ( " answer: " , res . output_text ); return res . output_text ; } This compiles. It passes tests. It ships. And it will quietly cost you four figures a month before anyone notices, because nothing in that log tells you the model burned 8,000 hidden reasoning tokens to produce a 40-token reply. That's the gap this article is about. AI calls are not regular HTTP calls. The interesting state isn't the response body - it's the messages you sent, the tools the model picked, the tokens it consumed (visible and otherwise), and the dollars that drained out of the budget. If your observability story is "we log the answer," you're flying a plane with one gauge and that gauge is the altimeter. Let's talk about what to actually capture. The four signals that matter Every AI system has the same four dimensions worth instrumenting, and most teams only track one or two of them: Logs - the request/response pair, the error, the latency. The boring stuff that traditional APM already covers. Prompts - the actual text that went in and the actual text that came out. Including system prompts, tool definitions, and history. Tool calls - which tool the model picked, with what arguments, what came back, in what order, with what retries. Cost - input tokens, output tokens, cached tokens, reasoning tokens, model, and the per-million-token price for each. Multiplied per user, per feature, per request. Lose any one of these and you're working blind on a different axis of the problem. Lose the cost signal and you wake up to a Slack message from finance. Lose the tool-call signal and you can't tell why your agent kept booking the wrong flight. Lose the prompt signal and a prod regression becomes a guessing game. Lose plain logs and you don't even know the call happened. The go
AI 资讯
Because in a Life-Threatening Situation, Every Millisecond Counts
Removing expf() from a fire detector: one header, 1.95x faster, zero accuracy loss A smoke detector is not a demo project. When it fires, someone either evacuates in time or doesn't. The firmware running on that microcontroller has one job, and it needs to do it without hesitation, without bloat, and without dependencies that can fail in unexpected ways. Last May 28th I published a bare-metal fire detection system built with Hasaki 刃先 — a neural network trainer that exports standalone C headers with no runtime, no Python, no TensorFlow. The model is a 12-8-4-1 MLP trained on 28,596 sensor readings. It fits in 3.8 kB of Flash and achieves 99.93% accuracy on held-out data, with a single missed fire event out of 3,599. But there was something in that header that bothered me. static inline float sigmoid ( float x ) { return 1 . 0 f / ( 1 . 0 f + expf ( - x )); } expf() . Right there in a life-safety application. On a microcontroller that may not have a hardware FPU. The problem with expf() on bare metal On processors with a hardware FPU — like the ESP32-C3 — expf() is fast. But the moment you deploy to an ATmega328P, an ATtiny85, or any Cortex-M0 target, that call becomes software floating-point. The CPU has to simulate the operation in firmware, cycle by cycle. It works. But it carries hidden cost: unpredictable latency, dependency on math.h , and a transcendental function sitting in the critical path of every single inference. For a smoke detector running at 1 Hz this might seem irrelevant. But inference latency compounds with sensor reads, normalization, and communication overhead. And more importantly — if you're deploying to a truly constrained target, expf() might be the difference between fitting in Flash or not. The fix: one header from kigu-quant kigu-quant(comming soon) is a new tool in the Rosito Bench ecosystem. It generates ready-to-include C headers for evaluating mathematical functions on microcontrollers — no FPU, no libm, no dependencies. One command: k
AI 资讯
Parallel AI Coding with Git Worktrees: Run Multiple Agents Without Conflicts
Parallel AI Coding with Git Worktrees: Run Multiple Agents Without Conflicts Most parallel AI development problems stem from a single architectural mistake: multiple agents sharing the same working directory. Teams spin up three Claude Code instances, point them at the same project folder, and watch as file writes collide, branch checkouts interrupt each other, and lock files corrupt. The symptom looks like a race condition. The root cause is filesystem design. Git worktrees solve this by giving each agent its own isolated working directory while sharing a single .git repository. This distinction is critical. Developers get parallel execution without the storage overhead of full clones, and agents operate on separate branches without stepping on each other's file handles. The pattern has existed since Git 2.5, but AI coding workflows finally make it essential infrastructure. The Collision Problem: Why Multiple AI Agents Can't Share a Working Directory When you run git checkout feature-A in a directory where another process is reading files, the filesystem state changes underneath that reader. The other process doesn't see atomic transitions—it sees partial writes, missing files, and inconsistent dependency graphs. TypeScript compilers fail with "Cannot find module" errors. Dev servers crash because watched files disappeared mid-read. Lock files from package managers become corrupted when two agents run npm install simultaneously on different branches with different dependency trees. The obvious solution—staggering agent execution so only one runs at a time—defeats the purpose of parallel development. Teams that try this pattern end up with AI agents waiting in queue, each one blocking the next until it finishes. The bottleneck shifts from human typing speed to serial execution, and the productivity gains evaporate. Full repository clones work but waste disk space. A 2GB monorepo cloned five times for five agents consumes 10GB of redundant Git objects. Sparse checkou
AI 资讯
Bob's CLI
A local-first AI coding CLI that adapts to you Discussion | Link
产品设计
CueBuddy
Record talking videos without manual scrolling Discussion | Link
产品设计
Otty
A Mac native and beautiful terminal emulator Discussion | Link
AI 资讯
MCP Java SDK – Build Model Context Protocol servers in Java
Hi HN, I built an open-source Java SDK for building Model Context Protocol servers: https://github.com/6000fish/mcp-java It is intended for Java developers who want to expose tools, resources, or prompts to MCP-compatible agents without implementing the protocol plumbing from scratch. The project includes: Core MCP server SDK stdio transport SSE transport Java API and annotation-based tool registration Spring Boot starter 5-minute quick-start example Copyable custom server template Ready-to-use MySQL and Redis MCP servers The SDK is available on Maven Central: <dependency> <groupId> io.github.6000fish </groupId> <artifactId> mcp-sdk </artifactId> <version> 0.1.1 </version> </dependency> <dependency> <groupId> io.github.6000fish </groupId> <artifactId> mcp-spring-boot-starter </artifactId> <version> 0.1.1 </version> </dependency> The MySQL and Redis servers are local stdio MCP servers, because database/cache connectors are usually safer to run inside the user's own environment instead of exposing credentials to a hosted remote endpoint. GitHub: https://github.com/6000fish/mcp-java Release: https://github.com/6000fish/mcp-java/releases/tag/v0.1.1 Feedback is welcome.
AI 资讯
Why Your AI Engineer Hire Costs 56% More Than You Budgeted
The Budget You Approved Isn't the Budget You'll Pay You approved $180K for a senior AI engineer. Eighteen months later, you've spent $282K and you're still not sure the hire is working out. This isn't unusual. It's the rule. Companies hiring AI engineers for the first time routinely underestimate total cost by 40–60%. Here's a breakdown of where that gap comes from — and why most founders don't see it until it's too late. The 56% Gap: Where It Comes From 1. Recruiting Costs Are Higher Than You Think (~12–18% of first-year salary) AI engineer recruiting isn't like standard software recruiting. Specialized headhunters charge 20–25% of first-year salary. Even if you find someone through your network, you'll spend founder or VP time on 15–30 hours of interviewing, plus take-home evals that the best candidates increasingly decline. If you use a staffing firm, add the markup. If you DIY it, add the opportunity cost. Typical recruiting overhead: $22,000–$40,000 per hire 2. Onboarding Takes Longer for AI Roles (~2–3 months of ramp) An AI engineer hired to build production agent systems isn't productive on day 1. They need to understand your domain, your data, your existing architecture, and your risk tolerance for AI-generated outputs. The ramp is real — most teams see 60–90 days before meaningful output. At $180K salary, two months of ramp is $30,000 in salary with limited ROI. Add engineering time for mentoring (typically 20% of a senior engineer's time during ramp), and you're adding another $15,000–$20,000. Ramp cost: $30,000–$50,000 3. Infrastructure Spend Scales With Experiments AI engineers experiment. That's the job. Every experiment has a GPU bill, an API bill, and a storage bill. Early-stage teams routinely see $3,000–$8,000/month in AI infrastructure spend once they've hired their first AI engineer — much of it from exploratory work that doesn't ship. Over a year: $36,000–$96,000 in infra costs that weren't in the original headcount budget 4. Tooling and Data Cos
开发者
What an 8kb Postgres read costs
submitted by /u/andreiross [link] [留言]
AI 资讯
Theker just raised $85M to build the factory robot that doesn’t specialize in anything
Unlike humanoid robots designed around a fixed form — think Boston Dynamics — Theker's machines are built to be reconfigured.
AI 资讯
Human in the Love
The world's first MCP dating app inside Claude. Discussion | Link
产品设计
Revi
On-device voice dictation. No cloud, no account. Discussion | Link
AI 资讯
Jeff Bezos’s Prometheus raises $12B to build an ‘artificial general engineer’ for the physical world
The new round values the physical AI startup that aims to automate heavy engineering and drug design at $41 billion.
AI 资讯
Apple’s Camera Chief Thinks AI Can Give You Superpowers
The generative features in iOS 27’s new Photos app will add fake pixels to some of your shots, but Apple’s Jon McCormack says the company isn’t using AI “for the sake of AI.”
AI 资讯
One Agent Identity Per Customer: Multi-Tenant Email
Provisioning a tenant-scoped email identity for your SaaS is one POST: curl --request POST \ --url "https://api.us.nylas.com/v3/connect/custom" \ --header "Authorization: Bearer <NYLAS_API_KEY>" \ --header "Content-Type: application/json" \ --data '{ "provider": "nylas", "workspace_id": "<WORKSPACE_ID>", "settings": { "email": "scheduling@customer-a.com" } }' No OAuth dance, no refresh token — just an address on a registered domain. The response comes back already valid: { "request_id" : "5967ca40-a2d8-4ee0-a0e0-6f18ace39a90" , "data" : { "id" : "b1c2d3e4-5678-4abc-9def-0123456789ab" , "provider" : "nylas" , "grant_status" : "valid" , "email" : "scheduling@customer-a.com" , "scope" : [], "created_at" : 1742932766 } } The data.id is a grant_id that works with every existing Nylas endpoint, and the account is live immediately. That's the primitive behind a multi-tenant pattern worth knowing: one Agent Account per customer, on each customer's own verified domain, all managed from a single application. (Agent Accounts are in beta, so the surface may shift before GA.) The architecture in one paragraph Your app runs scheduling@customer-a.com , scheduling@customer-b.com , and so on — same code path, different identities. Each account has its own policy, its own send quota, and its own sender reputation. A single application can manage accounts across an unlimited number of registered domains, so tenant count is a billing question, not an architectural one. Customer A's deliverability problems stay Customer A's; nothing they do contaminates Customer B's mail. Domains: register once, mint accounts forever The provisioning docs lay out two domain strategies you can mix freely in one application: Strategy Address format Setup Trial domain alias@<your-application>.nylas.email None — instant Your own domain alias@yourdomain.com MX + TXT records at the DNS provider For the per-customer pattern, each tenant brings their domain. You register it once per organization (picking the US
AI 资讯
Extract OTP Codes From Email, Automatically
What does your automation do when the login flow it's driving sends a six-digit code instead of a confirmation link? For most teams the honest answer is "a human goes and checks a shared inbox," which is a strange bottleneck to leave in the middle of an otherwise fully automated pipeline. There's a cleaner shape: the agent owns the mailbox the code lands in. With a Nylas Agent Account — a hosted mailbox controlled entirely through the API, currently in beta — the OTP email arrives, a webhook fires, your handler extracts the code, and whatever orchestrates the login gets it back. No human, no inbox-checking Slack message, no screen-scraping Gmail. Step one: make sure it's the right email A message.created webhook fires on every inbound message, so the first job is filtering down to the one that actually carries the code. The recipe uses two signals together — sender domain and a subject heuristic: app . post ( " /webhooks/otp " , async ( req , res ) => { res . status ( 200 ). end (); const event = req . body ; if ( event . type !== " message.created " ) return ; const msg = event . data . object ; if ( msg . grant_id !== AGENT_GRANT_ID ) return ; const sender = msg . from ?.[ 0 ]?. email ?? "" ; const subject = msg . subject ?? "" ; const senderMatches = sender . endsWith ( " @no-reply.example.com " ); const subjectLooksRight = /code|verif|one. ? time|passcode/i . test ( subject ); if ( ! senderMatches || ! subjectLooksRight ) return ; await handleOtp ( msg . id ); }); Neither check alone is enough. Sender-only matching trips on welcome emails from the same domain; subject-only matching trips on anything that mentions "verification." Regex first, LLM second Most OTP emails follow one of a few shapes: a standalone 4–8 digit number, or a code after a label like "Your code is:". Three patterns, tried in order from most to least specific, cover the vast majority of services: const patterns = [ / (?: code|passcode|one [\s - ]? time )[^\d]{0,20}(\d{4,8}) /i , // "Your code
AI 资讯
Ephemeral Inboxes: Spin Up a Mailbox Per Test Run
Two CI workers kick off at the same moment. Both sign up a test user, both poll the shared QA Gmail account for "the" verification email, and worker #7 grabs the message that belonged to worker #12. The test passes. The wrong test. You spend an afternoon staring at a green build that should've been red. Shared inboxes are the single biggest source of flakiness in email-dependent E2E tests, and every workaround — catch-all forwarding rules, label rules scoped per PR, OAuth tokens living on the runner — adds another moving part that breaks on its own schedule. The fix is structural: every test gets its own address, on infrastructure your suite provisions and destroys. One wildcard, infinite addresses The E2E email testing recipe sets this up with one CLI command: nylas inbound create e2e You get back an inbox ID and a wildcard pattern shaped like e2e-*@yourapp.nylas.email . From there, each test mints a unique address under the wildcard — e2e-<uuid>@yourapp.nylas.email — and there's nothing to provision per address. You don't pay or configure per address either; the wildcard is just a convention, so burn UUIDs freely. Mail flows through MX records hosted on the Nylas side, which means zero DNS work in your own zone (the tradeoff: addresses live under *.nylas.email ). The Playwright fixture is two pieces — an address minter and a poller: export const test = base . extend < Fixtures > ({ testEmail : async ({}, use ) => { await use ( `e2e- ${ randomUUID ()} @yourapp.nylas.email` ); }, pollInbox : async ({ testEmail }, use ) => { const poll = async ( timeoutMs = 30 _000 ) => { const deadline = Date . now () + timeoutMs ; while ( Date . now () < deadline ) { const out = execSync ( `nylas inbound messages ${ process . env . INBOX_ID } --json --limit 50` , ). toString (); const match = JSON . parse ( out ). find (( m ) => m . to . some (( t ) => t . email === testEmail ), ); if ( match ) return match ; await new Promise (( r ) => setTimeout ( r , 1500 )); } throw new Error (