netizen-cli 0.10.0__py3-none-any.whl
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.
- netizen_cli/__init__.py +3 -0
- netizen_cli/__main__.py +4 -0
- netizen_cli/admin/__init__.py +1 -0
- netizen_cli/admin/auth.py +928 -0
- netizen_cli/admin/errors.py +9 -0
- netizen_cli/admin/port_config.py +115 -0
- netizen_cli/admin/presentation.py +257 -0
- netizen_cli/admin/queries.py +337 -0
- netizen_cli/admin/static/admin.css +260 -0
- netizen_cli/admin/static/admin.js +2898 -0
- netizen_cli/admin/static/index.html +327 -0
- netizen_cli/admin/transport.py +935 -0
- netizen_cli/admin/web.py +2717 -0
- netizen_cli/bindings.py +3215 -0
- netizen_cli/builtin_skills.py +93 -0
- netizen_cli/cards/__init__.py +105 -0
- netizen_cli/cards/callbacks.py +565 -0
- netizen_cli/cards/controls.py +2273 -0
- netizen_cli/cards/defaults.py +213 -0
- netizen_cli/cards/model_info.py +80 -0
- netizen_cli/cards/questions.py +220 -0
- netizen_cli/cards/reply.py +2247 -0
- netizen_cli/cards/scheduled.py +836 -0
- netizen_cli/channel/__init__.py +1 -0
- netizen_cli/channel/completion_mentions.py +60 -0
- netizen_cli/channel/input_preparation.py +644 -0
- netizen_cli/channel/messages.py +57 -0
- netizen_cli/channel/ports.py +52 -0
- netizen_cli/channel/question_inputs.py +51 -0
- netizen_cli/channel/reactions.py +293 -0
- netizen_cli/channel/reply_presenter.py +1505 -0
- netizen_cli/channel/topics.py +70 -0
- netizen_cli/channel_app.py +6593 -0
- netizen_cli/cli.py +287 -0
- netizen_cli/cli_data.py +536 -0
- netizen_cli/cli_packages.py +526 -0
- netizen_cli/cli_services.py +651 -0
- netizen_cli/cli_setup.py +242 -0
- netizen_cli/cli_update.py +303 -0
- netizen_cli/cli_update_restore.py +53 -0
- netizen_cli/cli_update_worker.py +333 -0
- netizen_cli/codex_runtime.py +7125 -0
- netizen_cli/completion_mention.py +16 -0
- netizen_cli/database_migrations.py +218 -0
- netizen_cli/defaults/__init__.py +5 -0
- netizen_cli/defaults/models.py +39 -0
- netizen_cli/defaults/service.py +232 -0
- netizen_cli/defaults/store.py +260 -0
- netizen_cli/deployment/__init__.py +1 -0
- netizen_cli/deployment/restart_worker.py +134 -0
- netizen_cli/deployment/update_executor.py +258 -0
- netizen_cli/deployment/update_protocol.py +281 -0
- netizen_cli/domain.py +416 -0
- netizen_cli/error_messages.py +124 -0
- netizen_cli/experience.py +531 -0
- netizen_cli/feishu_app_onboarding.py +187 -0
- netizen_cli/feishu_app_permissions.py +123 -0
- netizen_cli/git_status.py +63 -0
- netizen_cli/image_inputs.py +579 -0
- netizen_cli/instance.py +84 -0
- netizen_cli/lark_app.py +125 -0
- netizen_cli/main.py +903 -0
- netizen_cli/management/__init__.py +83 -0
- netizen_cli/management/blocking_io.py +352 -0
- netizen_cli/management/chat_labels.py +266 -0
- netizen_cli/management/coordination.py +32 -0
- netizen_cli/management/service.py +2187 -0
- netizen_cli/management/updates.py +214 -0
- netizen_cli/markdown_images.py +78 -0
- netizen_cli/message_content.py +786 -0
- netizen_cli/message_history.py +643 -0
- netizen_cli/message_preparation.py +60 -0
- netizen_cli/message_projection.py +923 -0
- netizen_cli/migrations/__init__.py +1 -0
- netizen_cli/migrations/schema.py +103 -0
- netizen_cli/migrations/v14.py +438 -0
- netizen_cli/model_settings.py +269 -0
- netizen_cli/package_resources.py +22 -0
- netizen_cli/projects.py +327 -0
- netizen_cli/prompt_projection.py +327 -0
- netizen_cli/quoted_context.py +312 -0
- netizen_cli/resources/config.example.yaml +35 -0
- netizen_cli/resources/skills/netizen-lark/SKILL.md +64 -0
- netizen_cli/resources/skills/netizen-user-guide/SKILL.md +37 -0
- netizen_cli/resources/skills/netizen-user-guide/references/user-guide.md +842 -0
- netizen_cli/result_images.py +123 -0
- netizen_cli/runtime/__init__.py +1 -0
- netizen_cli/runtime/contracts.py +792 -0
- netizen_cli/runtime/name_writes.py +67 -0
- netizen_cli/runtime/thread_naming.py +451 -0
- netizen_cli/schedules/__init__.py +1 -0
- netizen_cli/schedules/mcp.py +535 -0
- netizen_cli/schedules/models.py +394 -0
- netizen_cli/schedules/scheduler.py +374 -0
- netizen_cli/schedules/service.py +766 -0
- netizen_cli/schedules/store.py +771 -0
- netizen_cli/sdk_gap_adapter.py +1151 -0
- netizen_cli/service_launcher.py +583 -0
- netizen_cli/session_settings.py +126 -0
- netizen_cli/settings.py +216 -0
- netizen_cli/skill_references.py +40 -0
- netizen_cli/terminal_cleanup.py +155 -0
- netizen_cli/turn_activity.py +688 -0
- netizen_cli/turn_files.py +812 -0
- netizen_cli/turn_patch_children.py +254 -0
- netizen_cli/turn_plan_observer.py +315 -0
- netizen_cli/user_questions.py +106 -0
- netizen_cli-0.10.0.dist-info/METADATA +18 -0
- netizen_cli-0.10.0.dist-info/RECORD +112 -0
- netizen_cli-0.10.0.dist-info/WHEEL +5 -0
- netizen_cli-0.10.0.dist-info/entry_points.txt +2 -0
- netizen_cli-0.10.0.dist-info/top_level.txt +1 -0
netizen_cli/lark_app.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""The instance's protected Lark application credentials, in CLI profile format.
|
|
2
|
+
|
|
3
|
+
This module uses only the standard library so installation and the service share
|
|
4
|
+
the same file contract without depending on the optional Lark CLI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import stat
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
LARK_APP_PROFILE = "netizen"
|
|
19
|
+
LARK_APP_RELATIVE_PATH = Path("lark-app/config.json")
|
|
20
|
+
_MAX_CONFIG_BYTES = 65_536
|
|
21
|
+
_APP_ID = re.compile(r"cli_[A-Za-z0-9_-]+")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LarkAppConfigError(ValueError):
|
|
25
|
+
"""A credential configuration failure that is safe to display."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True, slots=True)
|
|
29
|
+
class LarkAppCredentials:
|
|
30
|
+
app_id: str
|
|
31
|
+
app_secret: str = field(repr=False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _unique_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
35
|
+
result: dict[str, Any] = {}
|
|
36
|
+
for key, value in pairs:
|
|
37
|
+
if key in result:
|
|
38
|
+
raise LarkAppConfigError("Lark app configuration has duplicate JSON fields")
|
|
39
|
+
result[key] = value
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _validate_credentials(
|
|
44
|
+
app_id: Any, app_secret: Any, *, allow_incomplete: bool,
|
|
45
|
+
) -> LarkAppCredentials:
|
|
46
|
+
if not isinstance(app_id, str) or (
|
|
47
|
+
not _APP_ID.fullmatch(app_id) and not (allow_incomplete and app_id == "")
|
|
48
|
+
):
|
|
49
|
+
raise LarkAppConfigError("Lark app profile netizen requires a valid cli_ App ID")
|
|
50
|
+
if (
|
|
51
|
+
not isinstance(app_secret, str)
|
|
52
|
+
or app_secret.strip() != app_secret
|
|
53
|
+
or any(ord(character) < 0x20 for character in app_secret)
|
|
54
|
+
or (not app_secret and not allow_incomplete)
|
|
55
|
+
):
|
|
56
|
+
raise LarkAppConfigError("Lark app profile netizen requires a non-empty App Secret string")
|
|
57
|
+
return LarkAppCredentials(app_id=app_id, app_secret=app_secret)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_lark_app(
|
|
61
|
+
content: bytes, *, allow_incomplete: bool = False,
|
|
62
|
+
) -> LarkAppCredentials:
|
|
63
|
+
if len(content) > _MAX_CONFIG_BYTES:
|
|
64
|
+
raise LarkAppConfigError("Lark app configuration exceeds the size limit")
|
|
65
|
+
try:
|
|
66
|
+
config = json.loads(content, object_pairs_hook=_unique_object)
|
|
67
|
+
except (ValueError, UnicodeError):
|
|
68
|
+
raise LarkAppConfigError("Lark app configuration must contain valid, unambiguous JSON") from None
|
|
69
|
+
if not isinstance(config, dict) or not isinstance(config.get("apps"), list):
|
|
70
|
+
raise LarkAppConfigError("Lark app configuration requires an apps array")
|
|
71
|
+
profiles = [
|
|
72
|
+
app for app in config["apps"]
|
|
73
|
+
if isinstance(app, dict) and app.get("name") == LARK_APP_PROFILE
|
|
74
|
+
]
|
|
75
|
+
if len(profiles) != 1:
|
|
76
|
+
raise LarkAppConfigError("Lark app configuration requires exactly one profile named netizen")
|
|
77
|
+
profile = profiles[0]
|
|
78
|
+
if profile.get("brand") != "feishu":
|
|
79
|
+
raise LarkAppConfigError("Lark app profile netizen requires brand feishu")
|
|
80
|
+
return _validate_credentials(
|
|
81
|
+
profile.get("appId"), profile.get("appSecret"), allow_incomplete=allow_incomplete,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def load_lark_app(
|
|
86
|
+
path: str | Path, *, allow_incomplete: bool = False,
|
|
87
|
+
) -> LarkAppCredentials:
|
|
88
|
+
path = Path(path)
|
|
89
|
+
if not path.is_absolute():
|
|
90
|
+
raise LarkAppConfigError("Lark app configuration path must be absolute")
|
|
91
|
+
try:
|
|
92
|
+
descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
|
|
93
|
+
with os.fdopen(descriptor, "rb") as stream:
|
|
94
|
+
metadata = os.fstat(stream.fileno())
|
|
95
|
+
if (
|
|
96
|
+
not stat.S_ISREG(metadata.st_mode)
|
|
97
|
+
or metadata.st_uid != os.geteuid()
|
|
98
|
+
or metadata.st_mode & 0o077
|
|
99
|
+
):
|
|
100
|
+
raise LarkAppConfigError(
|
|
101
|
+
"Lark app configuration must be an owned regular file with permissions 0600 or stricter"
|
|
102
|
+
)
|
|
103
|
+
content = stream.read(_MAX_CONFIG_BYTES + 1)
|
|
104
|
+
except OSError:
|
|
105
|
+
raise LarkAppConfigError("Lark app configuration cannot be read; check its path and file permissions") from None
|
|
106
|
+
return parse_lark_app(content, allow_incomplete=allow_incomplete)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def encode_lark_app(app_id: str, app_secret: str) -> bytes:
|
|
110
|
+
credentials = _validate_credentials(app_id, app_secret, allow_incomplete=True)
|
|
111
|
+
config = {
|
|
112
|
+
"currentApp": LARK_APP_PROFILE,
|
|
113
|
+
"apps": [{
|
|
114
|
+
"name": LARK_APP_PROFILE,
|
|
115
|
+
"appId": credentials.app_id,
|
|
116
|
+
"appSecret": credentials.app_secret,
|
|
117
|
+
"brand": "feishu",
|
|
118
|
+
"defaultAs": "bot",
|
|
119
|
+
"users": [],
|
|
120
|
+
}],
|
|
121
|
+
}
|
|
122
|
+
content = (json.dumps(config, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
|
|
123
|
+
if len(content) > _MAX_CONFIG_BYTES:
|
|
124
|
+
raise LarkAppConfigError("Lark app configuration exceeds the size limit")
|
|
125
|
+
return content
|