@acedatacloud/skills 2026.628.1 → 2026.628.2

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.628.1",
3
+ "version": "2026.628.2",
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",
@@ -0,0 +1,16 @@
1
+ # Unattended Scheduled-Task Confirmation
2
+
3
+ Write-capable Skill helpers may support unattended scheduled-task execution with
4
+ a common `--unattended-confirm` flag. The flag must only bypass the normal
5
+ dry-run/confirmation flow when all platform-provided environment checks pass:
6
+
7
+ - `AICHAT_UNATTENDED_MODE=true`
8
+ - `AICHAT_ACTIVE_SKILL` matches the helper's Skill slug
9
+ - `AICHAT_ACTIVE_SKILL` is present in `AICHAT_UNATTENDED_ALLOWED_SKILLS`
10
+ - `AICHAT_UNATTENDED_EXPIRES_AT`, when present, is still in the future
11
+
12
+ If any check fails, the helper must return a dry-run style response and must not
13
+ perform the write/send/publish/delete action. The current Skills packaging does
14
+ not automatically bundle `_shared` files into each Skill, so helpers that need
15
+ runtime code should vendor a small guard module in their own `scripts/` folder
16
+ until packaging grows shared-runtime support.
@@ -7,11 +7,12 @@ import argparse
7
7
  import json
8
8
  import os
9
9
  import sys
10
- import time
11
10
  import urllib.error
12
11
  import urllib.parse
13
12
  import urllib.request
14
13
 
14
+ from unattended import unattended_confirm_allowed
15
+
15
16
 
16
17
  BASE_URL = os.environ.get("PERSONALWECHAT_BASE_URL", "").rstrip("/")
17
18
  API_TOKEN = os.environ.get("PERSONALWECHAT_API_TOKEN", "")
@@ -28,34 +29,6 @@ def _json(data) -> None:
28
29
  print(json.dumps(data, ensure_ascii=False, default=str))
29
30
 
30
31
 
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
-
59
32
  def request(method: str, path: str, *, params: dict | None = None, body: dict | None = None):
60
33
  if not BASE_URL:
61
34
  _die("PERSONALWECHAT_BASE_URL is not set. Reconnect the Personal WeChat connector.")
@@ -220,7 +193,7 @@ def main() -> None:
220
193
  _json(request_task("POST", "/api/search", body={"query": args.query}, timeout=120))
221
194
  elif args.cmd == "send":
222
195
  if args.unattended_confirm:
223
- allowed, reason = unattended_confirm_allowed()
196
+ allowed, reason = unattended_confirm_allowed(SKILL_SLUGS)
224
197
  if not allowed:
225
198
  _json({"dry_run": True, "target": args.target, "text": args.text, "error": "unattended_confirmation_denied", "reason": reason})
226
199
  return
@@ -0,0 +1,39 @@
1
+ """Unattended scheduled-task confirmation guard bundled with this Skill."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import time
8
+ from collections.abc import Iterable
9
+
10
+
11
+ def unattended_confirm_allowed(skill_slugs: Iterable[str]) -> tuple[bool, str]:
12
+ """Return whether the active Skill is pre-authorized for unattended writes."""
13
+
14
+ if os.environ.get("AICHAT_UNATTENDED_MODE") != "true":
15
+ return False, "not running in AceDataCloud unattended scheduled-task mode"
16
+
17
+ active_skill = os.environ.get("AICHAT_ACTIVE_SKILL", "")
18
+ allowed_skill_slugs = set(skill_slugs)
19
+ if active_skill not in allowed_skill_slugs:
20
+ return False, f"active skill {active_skill or '<empty>'!r} is not one of {sorted(allowed_skill_slugs)!r}"
21
+
22
+ raw_allowed = os.environ.get("AICHAT_UNATTENDED_ALLOWED_SKILLS", "[]")
23
+ try:
24
+ allowed = json.loads(raw_allowed)
25
+ except json.JSONDecodeError:
26
+ return False, "AICHAT_UNATTENDED_ALLOWED_SKILLS is not valid JSON"
27
+ if not isinstance(allowed, list) or active_skill not in allowed:
28
+ return False, f"skill {active_skill!r} is not pre-authorized for unattended confirmation"
29
+
30
+ expires_raw = os.environ.get("AICHAT_UNATTENDED_EXPIRES_AT", "")
31
+ if expires_raw:
32
+ try:
33
+ expires_at = int(expires_raw)
34
+ except ValueError:
35
+ return False, "AICHAT_UNATTENDED_EXPIRES_AT is invalid"
36
+ if expires_at < int(time.time()):
37
+ return False, "unattended authorization has expired"
38
+
39
+ return True, "ok"