feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)

Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
This commit is contained in:
Z User committed 2026-10-27 22:50:50 +00:00
1 parent 7acc0c5451
commit e45b68bb8b
1089 files changed
+260614

No files matched your search

+163
View File
@@ -0,0 +1,163 @@
---
name: image-search
version: 1.0.0
description: |
ZAI in-house image search service, exposed through the z-ai-web-dev-sdk CLI.
Retrieve real images from the web for any text query, with optional short
captions, and get back OSS-hosted direct URLs that are guaranteed reachable.
Use when the user wants to find, fetch, illustrate, or embed images — e.g.
"search for images of X", "find a picture of Y", "I need cover art for Z",
"give me reference photos of W", "插图", "配图", "找图", "找张图",
"搜张图", "搜图".
---
# image-search (ZAI in-house, via z-ai SDK)
Searches public web images for a natural-language query, re-hosts each hit on
OSS so the link is embeddable, and (optionally) attaches a short caption. The
backend is **ZAI's own self-developed image search service**.
You **do not call the backend directly**, and you **do not call the gateway
HTTP API directly**. Always go through the `z-ai image-search` subcommand
from the `z-ai-web-dev-sdk` CLI — that's the only supported entry point.
## When to activate
Trigger this skill when the user wants to:
- Find images for an article, slide deck, PPT, blog post, or report.
- Get reference photos / inspiration for a topic.
- Embed pictures into generated documents with stable URLs.
- Caption a set of images in Chinese or English.
Do **not** use this skill for:
- Generating new images from scratch — use `z-ai image` (image generation).
- Reverse image search ("what is this image of?").
- Searching images inside a private corpus — this hits public web sources.
## Prerequisites
**`z-ai-web-dev-sdk` installed**, providing the `z-ai` binary:
```bash
npm install -g z-ai-web-dev-sdk
# or: bun add -g z-ai-web-dev-sdk
```
## Invocation
```bash
z-ai image-search --query "<natural-language sentence>" [flags]
```
The CLI prints the JSON response (pretty-formatted) to stdout, or writes it
to `--output <path>` if supplied.
### Flags
| Flag | Default | Notes |
|---------------------|---------|-------|
| `--query`, `-q` | — | Required. Natural-language sentence describing what should be in the image. Prefer one coherent concept; avoid mixing unrelated keywords. |
| `--count`, `-c` | 5 | Number of images to return. Range 1–20. |
| `--gl` | `cn` | Region code for localization. Common: `cn`, `us`, `jp`, `kr`. |
| `--no-rank` | — | Disable captioning for a faster, caption-less response (default is on). |
| `--output`, `-o` | — | Optional: write the full JSON response to this path. |
| `--help`, `-h` | — | Show CLI help. |
### Examples
```bash
# Default search (5 images, cn region, captions on).
z-ai image-search -q "a cute orange tabby kitten playing with yarn" --count 5
# US region, no captioning (faster).
z-ai image-search -q "vintage red sports car on a mountain road" --count 5 --gl us --no-rank
# Chinese query — captions come back in Chinese.
z-ai image-search -q "中国传统水墨山水画" --count 5 -o results.json
```
## Choosing parameters
- **`--query`**: use a descriptive sentence, not a keyword list. The service
is tuned for natural language and returns more on-topic results that way.
Match the language to the audience: Chinese queries produce Chinese
captions, English queries produce English captions.
- **`--count`**: default to `5` for most asset-gathering tasks. Drop to
`1`–`3` when the user only needs one finalist; raise toward `10`–`20`
when building a moodboard or browsing options. Stay within `1`–`20`.
- **`--no-rank`**: turn captions off for moodboards or when latency matters;
leave them on when the user will pick images by reading the captions.
- **One concept per call**: for two unrelated subjects, fire two separate
invocations rather than concatenating keywords.
## Response shape
```json
{
"success": true,
"query": "cute orange tabby kitten",
"count": 5,
"ranked": true,
"results": [
{
"original_url": "https://sfile.chatglm.cn/images-ppt/<hash>.png",
"caption": "A cute orange tabby kitten playing with yarn on a rug.",
"source": "Unsplash",
"original_width": "750px",
"original_height": "500px"
}
]
}
```
Failure response (HTTP is still 200; check `success`):
```json
{
"success": false,
"query": "...",
"count": 0,
"ranked": true,
"results": [],
"error": "<human-readable reason>"
}
```
### Field reference
| Field | Type | Notes |
|-----------------------------|---------|-------|
| `success` | boolean | Always check this before reading `results`. |
| `query` | string | Echo of the input query. |
| `count` | integer | Number of images actually returned. |
| `ranked` | boolean | Whether captioning was applied. |
| `results[].original_url` | string | OSS-hosted direct image URL. Stable and embeddable. |
| `results[].caption` | string | Short caption. Present only when `ranked` is `true`. |
| `results[].source` | string | Original source site (e.g. `Unsplash`, `Pinterest`). |
| `results[].original_width` | string | Image width as `"NNNpx"`. |
| `results[].original_height` | string | Image height as `"NNNpx"`. |
| `error` | string | Present only on failure. |
## Operating tips
1. **Always present the OSS `original_url`, not the source site URL.** The
OSS link is the one that's guaranteed reachable; source pages may be
paywalled, geo-blocked, or deleted.
2. **Skip captioning for speed.** `--no-rank` typically cuts response time
by more than half. Use it when you'll caption results yourself.
3. **Be patient with timeouts.** A full call can take 90 seconds or more —
the upstream does image reachability probes, OSS upload, and captioning
one after another. Use a client-side timeout of at least 120 seconds.
4. **Region matters.** `gl=cn` biases toward Chinese-language sources;
`gl=us` toward English. Pick the one that matches the user's audience.
## Error handling
| Symptom | Likely cause | What to do |
|---------|--------------|------------|
| `Unknown command "image-search"` | SDK too old | Upgrade: `npm install -g z-ai-web-dev-sdk@latest`. |
| `API request failed with status 401` / `403` | Auth issue at the gateway | Tell the user — credentials are managed outside this skill. |
| `API request failed with status 502` | Upstream service unreachable | Retry; if it persists the in-house service is down. |
| Empty `results` but `success: true` | Query too narrow or upstream filtered everything | Broaden the query, raise `--count`, or change `--gl`. |