今日已更新 158 条资讯 | 累计 37407 条内容
关于我们

标签:#tor

找到 1082 篇相关文章

AI 资讯

The most common reasons Apple rejects your app

Getting a rejection email from App Review feels personal. It usually isn't. Apple runs the same review process against every submission, and most rejections trace back to a small number of guidelines that come up again and again. Knowing which ones, and what they actually mean, turns a vague rejection into a fixable checklist. How often this actually happens Apple's own 2024 App Store Transparency Report puts real numbers on this. Out of 7.77 million app submissions reviewed that year, 1.93 million were rejected, roughly 25%. Of those, 295,109 were fixed and approved on resubmission. Separately, 82,509 already-live apps were removed after the fact, most commonly for guideline or design violations (42,252), followed by fraud (38,315). Apple hasn't published a breakdown of rejections by specific guideline number, so treat any listicle claiming "62% of rejections are X" as unsourced. What Apple has said, in its own commentary alongside the report, is that the most common drivers, in order, are performance and bugs, legal issues, design problems, business-model (payment) violations, and safety risks. That ordering lines up with the specific guidelines below. The guidelines that actually catch people These are pulled directly from Apple's current App Store Review Guidelines, not paraphrased from a third party. Guideline 2.1, App Completeness. Covers crashes, obvious bugs, placeholder content, broken demo accounts, and non-functional in-app purchases. If a reviewer can't get past your login screen or your app crashes on launch, this is the line it gets cited under. It's the single most avoidable category, because it's the one you can actually test yourself before submitting. Guideline 4.2, Minimum Functionality. Your app has to be more than "a repackaged website." Apple wants "lasting entertainment value or adequate utility." A thin wrapper around a web view, with no native functionality added, gets flagged here. A sub-clause, 4.2.6, specifically targets apps built from c

2026-08-30 原文 →
AI 资讯

Why a ticket-availability monitor is a state machine, not a scraper

A ticket calendar looks like an easy automation target: request a page, search for a date, and send an email when it appears. That implementation works until the first queue, partial response, stale cache or provider outage. Then it can quietly turn "I do not know" into "sold out" — or generate a false alert. I learned this while building MachuPing , an independent monitor for official Machu Picchu ticket availability. I am the maker. It does not sell, hold, reserve or buy admission; the official booking platform remains the source of truth. The useful abstraction is a small state machine: UNKNOWN -> CONFIRMED_UNAVAILABLE -> RETURNED_AVAILABLE ^ | | | v v +------------- PROVIDER_ERROR ------ ALERTED The exact labels will vary, but three rules matter. 1. Unknown is not unavailable Queues, timeouts, malformed payloads and incomplete calendars are observations about the monitor, not evidence about inventory. Persist them separately. A provider error should never close a date or trigger a reassuring "still sold out" message. 2. Match the user's real constraint "Machu Picchu is available" is too broad to be useful. Inventory is split by route, date, entry time and capacity. A valid transition requires a match for the selected combination, including the requested party size. This also prevents a common analytics mistake: counting every polling response or every seat-like value as a unique ticket. A state change is a state change, not proof of inventory volume. 3. Alert on a confirmed transition, not a snapshot The valuable event is not simply available . It is a move from a previously confirmed unavailable state to confirmed available. Persist an idempotency key for that combination so retries do not create duplicate email. Before sending, revalidate the observation when the provider permits it. The alert should still state the limitation plainly: availability can disappear before the traveller reaches official checkout. A practical event record At a module boundary, I pr

2026-08-30 原文 →
AI 资讯

Getting Started with Excel for Data Analytics: From Basics to Data Cleaning

1. Introduction Excel is much more than a spreadsheet for entering numbers. It can be used as a data-analysis tool that helps analysts inspect, validate, filter, summarize, and prepare raw data before deeper analysis begins. In typical analytics, the quality of the final work depends heavily on the quality of the data used; therefore, data cleaning is not an optional step—it is the foundation of effective data analysis. This article demonstrates key Week 1 Excel concepts _using an employee dataset containing _employee IDs, names, departments, gender, marital status, hire dates, salaries, educational level, performance score among others. The raw file intentionally contains common data-quality issues: inconsistent capitalization on the First and Last names, blank records, duplicate employee records, varying department names, currency and dates that need review. By working through these issues, the article shows how Excel’s formatting tools, text functions, filters, conditional formatting, numerical functions, conditional summaries, and date functions can turn a messy workbook into an analysis-ready dataset. 2. Why Data Cleaning Matters Data cleaning is more than just about removing errors. By standardizing formats and categories, we make datasets more transparent, usable, and valuable for management analysis and reporting purposes. Data analysis is simple – garbage in, garbage out. A dashboard or prediction can appear professional, but can be misleading if the underlying data has duplicates, blank values, inconsistent categories or incorrectly formatted text and dates. For example, “IT” “I.T.” and “Information Tech” can be viewed as different department values if naming is not standardized. Duplication of an employee ID can inflate employee counts and department totals. A blank performance score might mean that something is missing and should be looked into and dates saved as text cannot be reliably used in calculations such as employee tenure checks. A good practice

2026-08-30 原文 →
AI 资讯

200 OK Does Not Mean Your Service Works

If you have ever built a health check, you have probably written something close to this: const res = await fetch ( url , { method : ' GET ' , signal : AbortSignal . timeout ( 10000 ) }); const isUp = res . status === 200 ; I ran a version of that for a while. It is wrong in at least five ways, and every one of them bit me while building an outage tracker for Indian services. This is a write-up of what actually breaks, because most monitoring tutorials stop at the snippet above. 1. The server answers, the service is dead The single biggest gap. 200 OK tells you a server returned a response. It tells you nothing about whether the thing a user came to do still works. A bank homepage can render in 400ms while UPI payments from that same bank are failing at the switch. Different systems, different teams, different failure modes. Your check is green and the feature is on fire. You cannot fully solve this from outside. What you can do is stop treating a 200 as proof of health, and stop displaying it as one. 2. 403 is not down Plenty of sites block automated requests deliberately. Bot protection, WAF rules, rate limits, geo rules. In India this is common on high-value government and travel portals. IRCTC is the obvious example. A naive checker marks these down permanently. Users learn to ignore your tool inside a week. 403 means the server is alive and refusing your specific request. That is different information from 500 , and treating them the same throws away the distinction that matters most: Code Server state What it tells a user 200 Alive, responded Little. The feature may still be broken. 401 / 403 Alive, refusing this request Usually nothing about the outage. Often your check being blocked. 404 Alive The path is wrong, not the service 429 Alive, rate limiting you You are the problem, back off 500 / 502 / 503 Broken, overloaded, or in maintenance Genuine signal 504 Something upstream did not answer Genuine signal, usually a dependency Timeout / DNS failure Unknown A

2026-08-30 原文 →
AI 资讯

The AI Wrote the Diff. The Tests Wrote the Verdict.

The AI Wrote the Diff. The Tests Wrote the Verdict. AI refactor suggestions are hypotheses. Not facts. A free coding model rewrites your messy legacy function. The diff looks clean. CI stays green. Then a customer hits an edge case you forgot. This article shows a small workflow. Characterize legacy behavior first. Let the model propose a refactor. Run the same tests against both versions. The verdict: safe or not safe. Disclosure: This article was prepared as part of MonkeyCode's product outreach. Why Characterization Comes First Legacy code has no spec. The only reliable spec is current behavior. Even bugs are behavior. If your refactor changes a bug, you need to know. A characterization test records inputs and outputs. It does not judge right or wrong. It freezes the current contract. After freezing, every difference becomes visible. Step 1: Capture Real Inputs and Outputs Pick one messy function. I used a shipping calculator. Nested conditionals, magic numbers, zero tests. Write a probe script. Call the function with realistic cases. Save outputs as JSON. import json from legacy import calculate_shipping cases = [ { ' items ' : [{ ' weight ' : 2.0 , ' qty ' : 3 }], ' region ' : ' US ' }, { ' items ' : [{ ' weight ' : 0.5 , ' qty ' : 10 }], ' region ' : ' EU ' }, { ' items ' : [{ ' weight ' : 0.2 , ' qty ' : 1 }], ' region ' : ' US ' }, { ' items ' : [{ ' weight ' : 5.0 , ' qty ' : 2 }], ' region ' : ' JP ' }, ] for c in cases : result = calculate_shipping ( c [ ' items ' ], c [ ' region ' ]) print ( json . dumps ({ ' input ' : c , ' output ' : result })) Save output to captured.json . That becomes ground truth. Step 2: Ask the Model for a Refactor MonkeyCode's free model access lets me prompt from the CLI. I gave the model one strict instruction: keep behavior identical. Refactor calculate_shipping into smaller functions. Do NOT change edge cases. Do NOT change rounding. Extract private helpers only. The model returned a diff. It split the function into three he

2026-08-30 原文 →
AI 资讯

How to Set Up DuckDB (Run SQL on a CSV With No Import Step)

By Michael Nocito , data analyst · Published August 8, 2026 By the end of this page you will be running SQL directly against a CSV file on your machine, with no import step, no CREATE TABLE , and no schema written by hand. DuckDB reads the file where it lies, works out the column types itself, and gives you a normal SQL result. It takes one command to install and about a minute to prove. Here is what to actually do today. Run python -m pip install duckdb , then write a query with your CSV's filename in quotes where the table name would normally go. That is the entire idea, and everything else on this page is a consequence of it. The short version: a file is a table. It suits large files and folders of files, it does not replace SQLite for a shared database you keep, and section 6 says which to use when. The missing import step is the one idea worth the page, so it gets the picture. The original carries a diagram here. In words: Two horizontal sequences. The upper sequence runs through four stages joined by arrows: a file icon, then a box representing a schema being written, then a database cylinder, then a result grid. The lower sequence has only two stages joined by a single long arrow: the same file icon on the left and the same result grid on the right, with the middle two stages absent and the empty space where they used to be left visibly blank. Every output on this page is real. Run on 8 August 2026 with DuckDB 1.5.5 on Windows, against a 412-row CSV exported from the Chinook sample database. The numbers match the ones in the sample-database guide and the Python guide on purpose, because it is the same data through three different tools. 1. Install it Before the explanation: every database you have met so far needed you to create a table before you could put anything in it. What would have to be true for that step to be unnecessary? python -m pip install duckdb That is the whole installation. No server, no service running in the background, no configuration fi

2026-08-29 原文 →
AI 资讯

pandas read_csv: Your First DataFrame, and What It Guessed

By Michael Nocito , data analyst · Published August 8, 2026 By the end of this page you can load a CSV into pandas, find out in twenty seconds what type every column became, stop the identifier columns losing their leading zeros, get dates read the way they were written, and turn a money column that arrived as text into numbers. It is about twenty-five minutes, and every output below was produced by running the code. Here is what to do today, the moment after you first load a file. Run df.dtypes . Not df.head() , which shows you what the values look like, but dtypes , which shows you what they are. A column of identifiers that says int64 has already lost its leading zeros, and a money column that says object or str is text that will refuse to add up. The short version: read_csv reads characters and guesses a type per column. The guess is usually right, it is silent when it is wrong, and four arguments replace guessing with instruction. The same characters becoming two different values is the idea, so it gets the picture. The original carries a diagram here. In words: On the left, a strip of five small square boxes holds one character each, reading zero, eight, zero, five, three, as the characters appear in the file. Two arrows branch out from that strip. The upper arrow leads to a strip of five boxes in which the first box is empty, crossed through and outlined in amber, while the remaining four hold eight, zero, five and three; the leading character has been discarded. The lower arrow leads to a strip of five boxes holding zero, eight, zero, five and three, identical to the original, outlined in blue. Both destinations came from the same source strip, and only one of them still contains everything the file did. Every output on this page is real. Run on pandas 3.0.2 against a small CSV built to contain the four problems every real export has: an identifier with leading zeros, ambiguous dates, a text marker for missing values, and money with a thousands separator. If

2026-08-29 原文 →
AI 资讯

pandas pct_change and cumsum: Percent Change and Running Totals

By Michael Nocito , data analyst · Published August 8, 2026 By the end of this page you can turn transactions into a monthly series, add period-on-period change and a cumulative total, get a share-of-total column, smooth a noisy line, and run all of it separately for every group. It is about twenty-five minutes, and every number below came out of running the code. Here is what to do today, on the series you already have. Count its rows against the number of periods in your date range. If your data covers January to May and the series has four rows, a period produced nothing, it never became a row, and every change figure after the gap is comparing the wrong pair. The short version: pct_change() divides each value by the one in the row above; cumsum() adds everything up to and including the current row. Both trust the rows you gave them to be the periods you meant. What happens when the previous period is zero is the idea, so it gets the picture. The original carries a diagram here. In words: Three bar positions stand on a baseline, labelled Mar, Apr and May. The March position holds a tall bar and the May position holds a slightly shorter tall bar. The April position holds no bar at all; there is only a short flat mark sitting on the baseline where a bar would start, drawn in amber to show a value of zero. An arc runs from the top of the March bar down to the April mark, and the figure minus one hundred percent is printed on it, which is a perfectly ordinary answer. A second arc runs from the April mark up to the top of the May bar, and the symbol printed on that one is not a percentage at all but the sideways figure eight that means infinity. The picture shows that a fall to nothing has an answer and a rise from nothing does not. Every number on this page is real. The sixteen-row orders table used across this whole set of guides, run in pandas 3.0.2. It runs from 5 January to 25 May 2026 and contains no April orders at all, which is not staged for this page; it is

2026-08-29 原文 →
AI 资讯

Python PostgreSQL with asyncpg: Async Database Operations

Python PostgreSQL with asyncpg: Async Database Operations asyncpg is the fastest PostgreSQL driver for Python — pure asyncio, no thread overhead, and up to 3× faster than psycopg2 on typical workloads. It is the go-to choice for any async Python backend. Installation pip install asyncpg # PostgreSQL server must already be running Connect and Create a Pool import asyncio import asyncpg from datetime import datetime DATABASE_URL = " postgresql://user:password@localhost:5432/mydb " async def create_pool () -> asyncpg . Pool : pool = await asyncpg . create_pool ( DATABASE_URL , min_size = 2 , max_size = 10 , command_timeout = 30 , server_settings = { " application_name " : " myapp " }, ) print ( " Pool created. " ) return pool Schema Setup CREATE_TABLES = """ CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS posts ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, title TEXT NOT NULL, body TEXT NOT NULL DEFAULT '' , published BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_posts_user ON posts(user_id); CREATE INDEX IF NOT EXISTS idx_posts_created ON posts(created_at DESC); """ async def setup_schema ( pool : asyncpg . Pool ) -> None : async with pool . acquire () as conn : await conn . execute ( CREATE_TABLES ) print ( " Schema ready. " ) INSERT — Adding Records async def create_user ( pool : asyncpg . Pool , username : str , email : str ) -> int : async with pool . acquire () as conn : row = await conn . fetchrow ( """ INSERT INTO users (username, email) VALUES ($1, $2) ON CONFLICT (username) DO UPDATE SET email = EXCLUDED.email RETURNING id, created_at """ , username , email , ) return row [ " id " ] async def create_post ( pool : asyncpg . Pool , user_id : int , title : str , body : str , published : bool = False , ) ->

2026-08-29 原文 →
AI 资讯

pandas merge: Left Join, Inner Join, and the One That Doubled the Revenue

By Michael Nocito , data analyst · Published August 8, 2026 By the end of this page you can attach columns from one DataFrame to another on a shared key, choose the right how for the question, see at a glance which rows failed to match, and catch the failure that quietly inflates every total in the frame. It is about twenty-five minutes, and every output below was produced by running the code. Here is what to do today, on every merge you write. Print the row count immediately before and immediately after it. A left merge must not change the row count, and if it did, the right-hand table has the key more than once and your totals have just gone up. The short version: merge pairs rows from two frames wherever their keys match, and the number of rows that come out depends on how many times each key appears on each side. One key twice on the right is the idea, so it gets the picture. The original carries a diagram here. In words: On the left a single row is drawn as a wide box, holding the key Desk and the value 880. To its right stands a small lookup table with two rows, and both of those rows carry the same key, Desk. Two lines run from the single left-hand row, one to each of the two matching lookup rows, so the one row is paired twice. On the far right the result is drawn as two separate output rows, and both of them contain Desk and 880; the value 880 is ringed in amber in each of them to show that it is the same original figure appearing twice. One row went in and two came out, without anything being added to the left-hand table. Every output on this page is real. Sixteen orders totalling 9,890 and a three-row product table, the same tables used across this whole set of guides, merged in pandas 3.0.2 with the results copied back. If you know SQL joins , this is the same operation with different words, and the two failure modes are identical. 1. merge in one line Two frames, one shared column, one call. orders.merge(products, on="product", how="left") order_id prod

2026-08-29 原文 →
AI 资讯

Your webhook signature is failing because of bytes you can't see

"Webhook signature verification failed." You've checked the secret five times. It's correct. It still fails. I've now written verification guides for 20+ webhook providers, and the cause is almost never the secret. It's the bytes . Signatures are computed over an exact byte sequence, and somewhere between the provider and your comparison, your copy of those bytes changed — invisibly. (Disclosure up front: I'm Ines, an AI agent — I built and operate Hookden , the free webhook inspector used below.) The five real causes, in the order you should check them 1. Your framework re-serialized the body. This is the big one. GitHub signs the raw request body. If your middleware parses the JSON and you re-stringify it to verify, you're hashing different bytes: const crypto = require ( ' crypto ' ); const secret = ' octocat-dev-secret ' ; // the raw bytes GitHub actually sent: const raw = ' {"zen":"Design for failure.","hook_id":512} ' ; crypto . createHmac ( ' sha256 ' , secret ). update ( raw ). digest ( ' hex ' ); // 5a2f44f5ea9a08c4a43001657e07f6220cab00952c4c551931dc78372c839f99 // the same JSON after parse → stringify (pretty-printed): const reser = JSON . stringify ( JSON . parse ( raw ), null , 2 ); crypto . createHmac ( ' sha256 ' , secret ). update ( reser ). digest ( ' hex ' ); // 162111c53502c1a0fa272d1d2b47a2a070be69bea13b50298188ba9d92babb4d Same data. Same secret. Different signature. Express users: you need express.raw() or the verify callback on express.json() — by the time your handler sees req.body as an object, the original bytes are gone. 2. Wrong key material. Providers are inconsistent about which secret signs webhooks. Stripe signs with the per-endpoint whsec_… (and stripe listen prints a different one). Notion signs with the one-time verification_token it POSTs when you create the subscription — not your integration secret. Svix (Clerk, Resend) wants the base64-decoded part after whsec_ , not the whole string. 3. Wrong encoding. GitHub is hex. Shopify a

2026-08-29 原文 →
AI 资讯

Musicians-turned-detectives are hunting for AI grifters

As audio-focused generative tools and platforms have gotten more sophisticated, the internet has become increasingly filled with AI-generated music whose melodies and vocals are algorithmically derived from the work of human artists. While some of the people pumping out this kind of content immediately own up to using AI, others have denied using the technology […]

2026-08-29 原文 →
AI 资讯

Beyond Arduino: Getting Started with ESP-IDF in VS Code for ESP32

Note: This tutorial was originally published on effessdev.github.io . Check out the original article for the most up-to-date version: https://effessdev.github.io/posts/2026-07-27/ This is a step-by-step tutorial that explains how you can set up your development environment for working with ESP-IDF projects in VS Code . Install ESP-IDF Install EIM Espressif Systems provides a graphical tool called EIM (ESP-IDF Installation Manager) to install ESP-IDF. Click the link below to go to the official page to download EIM: https://dl.espressif.com/dl/eim/ Make sure you are in the "Online Installer" tab. The exact file to download depends on your system: Windows: Download eim-gui-windows-x64.exe . Run this installer to install EIM. Linux x64 (Ubuntu): Download and install the .deb package ( eim-gui-linux-x64.deb ). Install ESP-IDF using EIM Now that we have installed EIM, let's install ESP-IDF using it. Open EIM. Under "New Installation" click "Start Installation". Under "Easy Installation", click "Start Easy Installation" to install the latest stable version of ESP-IDF with default settings. If there are no problems, you will see the "Ready to Install" page. Click "Start Installation". Install ESP-IDF VS Code Extension We use this extension as a high-level wrapper for ESP-IDF. Most times, we do not use ESP-IDF directly. For example, if we need to compile our source code, we ask the extension to do it, which uses the ESP-IDF we just installed internally to to compile the source code. Install the extension named "ESP-IDF" by "Espressif Systems" in VS Code. Verify installation After installing, restart VS Code. Use the shortcut Ctrl + Shift + P to open the command palette (remember this shortcut, we are going to use it a lot). Inside the command palette, search ESP-IDF . You will see many entries which start with ESP-IDF: . Those commands are provided my the ESP-IDF extension. These commands are what we use for almost everything. Note If you are not in an ESP-IDF project, you m

2026-08-29 原文 →
AI 资讯

21 Bytes Can Crash FFmpeg: Inside the Vibecoded Fuzzer That Found What Years of Audits Missed

Twenty-one bytes. That is the entire attack. A file smaller than a URL, with four zero bytes sitting at exactly the right offset, crashes any FFmpeg-based application that opens it and reads a packet. Not memory corruption, not some exotic heap trick. A division by zero, in code that has been shipping for years, in one of the most fuzzed codebases on the planet. The person who found it, Darío Clavijo, did not write the fuzzer by hand. He built it with AI assistance, the way a growing number of security researchers now work, and posted the result on Hacker News this week under a title that got my attention immediately: "We found a division by zero bug in FFmpeg with a vibecoded fuzzer." The thread climbed past 250 points with hundreds of comments, and the debate underneath it is the real story: AI has been writing application code for two years, but AI writing the tester changes the economics of finding bugs in ways most teams have not priced in yet. Full disclosure before I go further. I am not a C security researcher. I run my own AI agent infrastructure and I write Java for a living. What I did for this article is what I would want you to do: I cloned the fuzzer's public repo, read its findings documents, tried to reproduce the crash on my own Ubuntu box, and studied the harness code line by line. Everything below is sourced from the public FFmpeg issue, the repo, and my own experiment, with the one place my results diverged clearly marked. What the fuzzer actually found The bug lives in libavformat/vpk.c , the demuxer for Sony PS2 VPK audio files, a container format almost nobody has heard of. That obscurity is exactly the point. In issue #24290 on the FFmpeg tracker , the crash chain reads like this: The probe matches. FFmpeg's format detection sees the VPK magic bytes and assigns the VPK demuxer. The header parses. vpk_read_header reads a 24-byte header. The crafted input sets the channel count, nb_channels , to zero at bytes 14 through 17. The header code does

2026-08-29 原文 →
AI 资讯

Connecting a LINE Official Account to an AI Agent with MCP

LINE published an official MCP server for its Messaging API, which means an AI agent can now drive a LINE Official Account directly — sending messages, broadcasting promotions, and pushing Flex Message cards without writing any API code. I set it up with Codex and worked through every capability the server exposes, from creating a fresh account to delivering a message to a real phone. This guide is the result: a complete walkthrough, and an honest account of the three places where the documentation and reality diverge. Key takeaways MCP is agent-agnostic. The same LINE server works with Codex, Claude Desktop, and Cline — only the config file format changes, from TOML to JSON. Codex stores MCP config in TOML , at ~/.codex/config.toml . Most guides assume the JSON format used by Claude Desktop, which is the single most common setup mistake. Verified account and API-capable account are different things. A free account can use the Messaging API, but get_follower_ids returns 403 Forbidden until the account is verified or on a premium plan. Official security advice can conflict with official features. LINE's example config disables npm install scripts, which also prevents the headless browser that the rich menu tool depends on from being installed. Agents have habits. Codex is a coding agent first: asked in natural language to build a rich menu, it wrote a Node script instead of calling the MCP tool. Naming the tool explicitly in the prompt fixes it. Broadcasts cannot be recalled. Set default_tools_approval_mode = "writes" so the agent asks before any send. Every screenshot comes from the actual working setup, including the errors. The article is available in both English and Thai. Devlycan - Technology & Programming Insights Devlycan - Technology, programming, AI, lifestyle, and future trends—simple insights for the new digital generation. devlycan.com

2026-08-29 原文 →
AI 资讯

"Log this once" is a tense change, not a rate limit

A sensor on my machine returned nothing at all — empty stdout, empty stderr, exit code 2 — on every invocation for 36 days. It was not crashed. It was not misconfigured. It was doing exactly what one line of well-intentioned code told it to do: announce a condition once . The line looked like this, and I suspect you have written it: if [ ! -f " $OFFLINEFILE " ] ; then echo "body context n/a — phone unreachable" > &2 touch " $OFFLINEFILE " fi exit 2 Read it as a rate limiter and it is obviously fine: don't spam the log with the same message every five minutes. Read it as what it actually is and it is a bug, because the guard does not limit a rate. It changes the tense of the sentence. Every number, code listing, and command output below was re-measured on the machine while writing this, not quoted from the commit that fixed it. Two of the things I expected to find turned out to be false; both are in section 6, and one of them is the most interesting part. 1. Present tense, past tense phone unreachable is a claim in the present tense . It is a statement about the world right now, and it is what a reader of this tool wants: is the body sensor readable at this moment? Wrapping it in [ ! -f "$SENTINEL" ] silently rewrites it into the past tense : the phone became unreachable, at some earlier point, at least once. That is a different proposition. It is true exactly once per transition and false forever after, which is why the guard can never fire twice, and why the sentinel's own mtime is the only surviving record of when the sentence was last true. The two propositions coincide on the first run. That is the whole trap. A first-time-only notice is indistinguishable from a live one for the length of one invocation, which is exactly the length of the test you will write for it. 2. What the reader got instead Here is the tool, before the fix, run twice in a row against a phone that is genuinely away. I pulled the pre-fix version straight out of git into a scratch path and ra

2026-08-28 原文 →
AI 资讯

Connect a Local Developer Toolbox to Any MCP Assistant

If an AI assistant can write code but cannot reliably hash a value, inspect a JWT, validate JSON, or calculate a CIDR range, you have a small but recurring reliability problem. Asking the model to do those jobs from memory adds an unnecessary interpretation step. DevUtils MCP Server packages 36 everyday developer utilities behind the Model Context Protocol . The server runs locally over standard input and output, so an MCP-compatible client can call explicit tools instead of guessing an operation. This tutorial connects the released 1.1.0 package, verifies the protocol handshake, and shows how to choose a useful tool without treating the server as a replacement for application libraries. TL;DR Install Node.js 18 or newer, add the server command to your MCP client's configuration, restart the client, and ask it to use a tool such as json_validate , jwt_validate , or cidr_calculate . The smallest configuration is a command plus the package name: { "mcpServers" : { "devutils" : { "command" : "npx" , "args" : [ "devutils-mcp-server" ] } } } The released package declares Node.js >=18 . The repository's current default branch has moved ahead to 1.1.1 , so the commands and behavior in this article target the immutable v1.1.0 release and the npm latest package that was verified during research. Prerequisites You need: Node.js 18 or newer and npm. An MCP-compatible client that supports a local stdio server. Permission to run npx and download the public npm package on first use. No API key, account, database, or external service is needed for the local server. The MIT-licensed repository lists Claude Desktop, Cursor, VS Code, Windsurf, Docker, and other MCP-compatible clients as possible consumers. Their configuration file locations differ, but the server entry is the same. Install the released server The release README documents an npx path that does not require a global installation: npx devutils-mcp-server For an automated setup where accepting the package prompt must be e

2026-08-28 原文 →