@acedatacloud/skills 2026.627.2 → 2026.628.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.627.2",
3
+ "version": "2026.628.0",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -135,7 +135,9 @@ Search drives the WeChat UI, so it may be slower than local DB history reads.
135
135
 
136
136
  ## Sending Messages — GATED
137
137
 
138
- `send` dry-runs by default. It never sends unless `--confirm` is present.
138
+ `send` dry-runs by default. It never sends unless `--confirm` is present, or
139
+ unless an AceDataCloud scheduled task pre-authorized this Skill and you use
140
+ `--unattended-confirm`.
139
141
 
140
142
  ```bash
141
143
  python3 $WX send "Alice" "今晚 8 点开会吗?"
@@ -152,11 +154,32 @@ python3 $WX send "Alice" "今晚 8 点开会吗?" --confirm
152
154
  Never add `--confirm` in the first attempt. Never infer consent from vague text.
153
155
  The user must clearly approve sending this exact message.
154
156
 
157
+ ### Scheduled-task unattended confirmation
158
+
159
+ When running inside an AceDataCloud scheduled task, the platform may pre-authorize
160
+ specific Skills for unattended execution. If all of these are true:
161
+
162
+ - `AICHAT_UNATTENDED_MODE=true`
163
+ - `AICHAT_ACTIVE_SKILL` is `personal-wechat` or `acedatacloud/personal-wechat`
164
+ - `AICHAT_ACTIVE_SKILL` appears in `AICHAT_UNATTENDED_ALLOWED_SKILLS`
165
+
166
+ then the user has pre-authorized this Skill for that scheduled task. In that
167
+ case, use:
168
+
169
+ ```bash
170
+ python3 $WX send "Alice" "今晚 8 点开会吗?" --unattended-confirm
171
+ ```
172
+
173
+ If the helper returns `unattended_confirmation_denied`, do not retry with
174
+ `--confirm`; report the dry-run and explain that the task needs this Skill to be
175
+ selected in its unattended authorization settings.
176
+
155
177
  ## Safety Rules
156
178
 
157
179
  - Never print `PERSONALWECHAT_API_TOKEN`.
158
180
  - Treat `PERSONALWECHAT_BASE_URL + API_TOKEN` as full remote control of the user's WeChat.
159
- - For any write/send operation: dry-run first, ask for explicit approval, then re-run with `--confirm`.
181
+ - For normal chat write/send operations: dry-run first, ask for explicit approval, then re-run with `--confirm`.
182
+ - For scheduled-task unattended writes: use `--unattended-confirm` only when the platform env says this Skill is pre-authorized.
160
183
  - Do not call logout/restart endpoints from the skill unless the user explicitly asks to repair the Wisdom service.
161
184
  - If Wisdom returns 503 for history, run `python3 $WX refresh-history` once, then retry the read.
162
185
  - If the server is unreachable, ask the user to check the Windows host / security group / port 8000.
@@ -176,4 +199,4 @@ The helper wraps these Wisdom endpoints:
176
199
  - `POST /api/messages/history/query`
177
200
  - `POST /api/messages/history/refresh`
178
201
  - `POST /api/search`
179
- - `POST /api/messages/send` (only after `--confirm`)
202
+ - `POST /api/messages/send` (only after `--confirm` or verified `--unattended-confirm`)
@@ -7,6 +7,7 @@ import argparse
7
7
  import json
8
8
  import os
9
9
  import sys
10
+ import time
10
11
  import urllib.error
11
12
  import urllib.parse
12
13
  import urllib.request
@@ -15,6 +16,7 @@ import urllib.request
15
16
  BASE_URL = os.environ.get("PERSONALWECHAT_BASE_URL", "").rstrip("/")
16
17
  API_TOKEN = os.environ.get("PERSONALWECHAT_API_TOKEN", "")
17
18
  MAX_TEXT_CHARS = 800
19
+ SKILL_SLUGS = {"personal-wechat", "acedatacloud/personal-wechat"}
18
20
 
19
21
 
20
22
  def _die(message: str, code: int = 1) -> None:
@@ -26,6 +28,34 @@ def _json(data) -> None:
26
28
  print(json.dumps(data, ensure_ascii=False, default=str))
27
29
 
28
30
 
31
+ def unattended_confirm_allowed() -> tuple[bool, str]:
32
+ if os.environ.get("AICHAT_UNATTENDED_MODE") != "true":
33
+ return False, "not running in AceDataCloud unattended scheduled-task mode"
34
+
35
+ active_skill = os.environ.get("AICHAT_ACTIVE_SKILL", "")
36
+ if active_skill not in SKILL_SLUGS:
37
+ return False, f"active skill {active_skill or '<empty>'!r} is not personal-wechat"
38
+
39
+ raw_allowed = os.environ.get("AICHAT_UNATTENDED_ALLOWED_SKILLS", "[]")
40
+ try:
41
+ allowed = json.loads(raw_allowed)
42
+ except json.JSONDecodeError:
43
+ return False, "AICHAT_UNATTENDED_ALLOWED_SKILLS is not valid JSON"
44
+ if not isinstance(allowed, list) or active_skill not in allowed:
45
+ return False, f"skill {active_skill!r} is not pre-authorized for unattended confirmation"
46
+
47
+ expires_raw = os.environ.get("AICHAT_UNATTENDED_EXPIRES_AT", "")
48
+ if expires_raw:
49
+ try:
50
+ expires_at = int(expires_raw)
51
+ except ValueError:
52
+ return False, "AICHAT_UNATTENDED_EXPIRES_AT is invalid"
53
+ if expires_at < int(time.time()):
54
+ return False, "unattended authorization has expired"
55
+
56
+ return True, "ok"
57
+
58
+
29
59
  def request(method: str, path: str, *, params: dict | None = None, body: dict | None = None):
30
60
  if not BASE_URL:
31
61
  _die("PERSONALWECHAT_BASE_URL is not set. Reconnect the Personal WeChat connector.")
@@ -127,6 +157,7 @@ def main() -> None:
127
157
  send.add_argument("target")
128
158
  send.add_argument("text")
129
159
  send.add_argument("--confirm", action="store_true")
160
+ send.add_argument("--unattended-confirm", action="store_true")
130
161
 
131
162
  refresh = sub.add_parser("refresh-history")
132
163
  refresh.set_defaults(cmd="refresh-history")
@@ -166,8 +197,13 @@ def main() -> None:
166
197
  elif args.cmd == "search":
167
198
  _json(request("POST", "/api/search", body={"query": args.query}))
168
199
  elif args.cmd == "send":
169
- if not args.confirm:
170
- _json({"dry_run": True, "target": args.target, "text": args.text, "note": "Re-run with --confirm only after the user explicitly approves sending this exact message."})
200
+ if args.unattended_confirm:
201
+ allowed, reason = unattended_confirm_allowed()
202
+ if not allowed:
203
+ _json({"dry_run": True, "target": args.target, "text": args.text, "error": "unattended_confirmation_denied", "reason": reason})
204
+ return
205
+ elif not args.confirm:
206
+ _json({"dry_run": True, "target": args.target, "text": args.text, "note": "Re-run with --confirm after explicit user approval, or --unattended-confirm when this Skill is pre-authorized for an AceDataCloud scheduled task."})
171
207
  return
172
208
  _json(request("POST", "/api/messages/send", body={"target": args.target, "type": "text", "text": args.text}))
173
209
  elif args.cmd == "refresh-history":