hap-cli 0.5.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.
- hap_cli/README.md +194 -0
- hap_cli/README_CN.md +601 -0
- hap_cli/__init__.py +3 -0
- hap_cli/commands/__init__.py +1 -0
- hap_cli/commands/ai_cmd.py +224 -0
- hap_cli/commands/app_cmd.py +308 -0
- hap_cli/commands/calendar_cmd.py +138 -0
- hap_cli/commands/chat_cmd.py +101 -0
- hap_cli/commands/config_cmd.py +169 -0
- hap_cli/commands/contact_cmd.py +125 -0
- hap_cli/commands/department_cmd.py +168 -0
- hap_cli/commands/group_cmd.py +128 -0
- hap_cli/commands/instance_cmd.py +310 -0
- hap_cli/commands/node_cmd.py +538 -0
- hap_cli/commands/optionset_cmd.py +99 -0
- hap_cli/commands/page_cmd.py +102 -0
- hap_cli/commands/plugin_cmd.py +133 -0
- hap_cli/commands/post_cmd.py +155 -0
- hap_cli/commands/record_cmd.py +228 -0
- hap_cli/commands/role_cmd.py +221 -0
- hap_cli/commands/workflow_cmd.py +284 -0
- hap_cli/commands/worksheet_cmd.py +342 -0
- hap_cli/context.py +43 -0
- hap_cli/core/__init__.py +1 -0
- hap_cli/core/ai.py +133 -0
- hap_cli/core/app.py +307 -0
- hap_cli/core/auth.py +219 -0
- hap_cli/core/calendar_mod.py +114 -0
- hap_cli/core/chat.py +73 -0
- hap_cli/core/contact.py +85 -0
- hap_cli/core/department.py +131 -0
- hap_cli/core/flow_node.py +1001 -0
- hap_cli/core/group.py +99 -0
- hap_cli/core/instance.py +572 -0
- hap_cli/core/optionset.py +112 -0
- hap_cli/core/page.py +138 -0
- hap_cli/core/plugin.py +87 -0
- hap_cli/core/post.py +118 -0
- hap_cli/core/record.py +268 -0
- hap_cli/core/role.py +227 -0
- hap_cli/core/session.py +348 -0
- hap_cli/core/workflow.py +556 -0
- hap_cli/core/worksheet.py +403 -0
- hap_cli/hap_cli.py +105 -0
- hap_cli/skills/SKILL.md +383 -0
- hap_cli/skills/__init__.py +0 -0
- hap_cli/tests/__init__.py +1 -0
- hap_cli/tests/test_core.py +1824 -0
- hap_cli/tests/test_full_e2e.py +136 -0
- hap_cli/tests/test_integration.py +805 -0
- hap_cli/utils/__init__.py +1 -0
- hap_cli/utils/formatting.py +111 -0
- hap_cli/utils/options.py +10 -0
- hap_cli-0.5.0.dist-info/METADATA +223 -0
- hap_cli-0.5.0.dist-info/RECORD +58 -0
- hap_cli-0.5.0.dist-info/WHEEL +5 -0
- hap_cli-0.5.0.dist-info/entry_points.txt +2 -0
- hap_cli-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""CLI commands for AI assistant and service management."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from hap_cli.context import pass_context
|
|
8
|
+
from hap_cli.core import ai as ai_mod
|
|
9
|
+
from hap_cli.utils.formatting import output_json, output_kv
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def ai():
|
|
14
|
+
"""AI assistant and service management."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ── AI Assistants ────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@ai.command("assistants")
|
|
22
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
23
|
+
@click.option("--page-size", "-n", default=20, help="Items per page")
|
|
24
|
+
@click.option("--page", default=1, help="Page number")
|
|
25
|
+
@pass_context
|
|
26
|
+
def ai_assistants(ctx, project_id, page_size, page):
|
|
27
|
+
"""List AI assistants."""
|
|
28
|
+
try:
|
|
29
|
+
session = ctx.get_session()
|
|
30
|
+
pid = project_id or session.default_project_id
|
|
31
|
+
if not pid:
|
|
32
|
+
raise click.UsageError("Project ID required.")
|
|
33
|
+
data = ai_mod.get_assistants(session, pid, page_index=page, page_size=page_size)
|
|
34
|
+
ctx.output(data, lambda d: output_json(d))
|
|
35
|
+
except Exception as e:
|
|
36
|
+
ctx.handle_error(e)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@ai.command("assistant-get")
|
|
40
|
+
@click.argument("assistant_id")
|
|
41
|
+
@pass_context
|
|
42
|
+
def ai_assistant_get(ctx, assistant_id):
|
|
43
|
+
"""Get AI assistant detail."""
|
|
44
|
+
try:
|
|
45
|
+
session = ctx.get_session()
|
|
46
|
+
data = ai_mod.get_assistant(session, assistant_id)
|
|
47
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
48
|
+
except Exception as e:
|
|
49
|
+
ctx.handle_error(e)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@ai.command("assistant-create")
|
|
53
|
+
@click.option("--config", "-c", required=True, help="Assistant config as JSON string")
|
|
54
|
+
@pass_context
|
|
55
|
+
def ai_assistant_create(ctx, config):
|
|
56
|
+
"""Create or update an AI assistant (JSON config)."""
|
|
57
|
+
try:
|
|
58
|
+
session = ctx.get_session()
|
|
59
|
+
data = ai_mod.upsert_assistant(session, json.loads(config))
|
|
60
|
+
ctx.output(data, lambda d: click.echo(f"Assistant created/updated: {d}"))
|
|
61
|
+
except json.JSONDecodeError as e:
|
|
62
|
+
ctx.handle_error(click.UsageError(f"Invalid JSON: {e}"))
|
|
63
|
+
except Exception as e:
|
|
64
|
+
ctx.handle_error(e)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@ai.command("assistant-delete")
|
|
68
|
+
@click.argument("assistant_id")
|
|
69
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
70
|
+
@pass_context
|
|
71
|
+
def ai_assistant_delete(ctx, assistant_id, yes):
|
|
72
|
+
"""Delete an AI assistant."""
|
|
73
|
+
try:
|
|
74
|
+
if not yes:
|
|
75
|
+
click.confirm(f"Delete assistant {assistant_id}?", abort=True)
|
|
76
|
+
session = ctx.get_session()
|
|
77
|
+
data = ai_mod.delete_assistant(session, assistant_id)
|
|
78
|
+
ctx.output(data, lambda d: click.echo("Assistant deleted."))
|
|
79
|
+
except Exception as e:
|
|
80
|
+
ctx.handle_error(e)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@ai.command("assistant-status")
|
|
84
|
+
@click.argument("assistant_id")
|
|
85
|
+
@click.option("--status", "-s", required=True, type=int, help="Status: 1=enabled, 0=disabled")
|
|
86
|
+
@pass_context
|
|
87
|
+
def ai_assistant_status(ctx, assistant_id, status):
|
|
88
|
+
"""Enable or disable an AI assistant."""
|
|
89
|
+
try:
|
|
90
|
+
session = ctx.get_session()
|
|
91
|
+
data = ai_mod.set_assistant_status(session, assistant_id, status)
|
|
92
|
+
ctx.output(data, lambda d: click.echo(f"Status set to {status}."))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
ctx.handle_error(e)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ── Knowledge Base ───────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@ai.command("knowledge-list")
|
|
101
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
102
|
+
@pass_context
|
|
103
|
+
def ai_knowledge_list(ctx, project_id):
|
|
104
|
+
"""List knowledge bases."""
|
|
105
|
+
try:
|
|
106
|
+
session = ctx.get_session()
|
|
107
|
+
pid = project_id or session.default_project_id
|
|
108
|
+
if not pid:
|
|
109
|
+
raise click.UsageError("Project ID required.")
|
|
110
|
+
data = ai_mod.get_knowledge_bases(session, pid)
|
|
111
|
+
ctx.output(data, lambda d: output_json(d))
|
|
112
|
+
except Exception as e:
|
|
113
|
+
ctx.handle_error(e)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@ai.command("knowledge-get")
|
|
117
|
+
@click.argument("kb_id")
|
|
118
|
+
@pass_context
|
|
119
|
+
def ai_knowledge_get(ctx, kb_id):
|
|
120
|
+
"""Get knowledge base detail."""
|
|
121
|
+
try:
|
|
122
|
+
session = ctx.get_session()
|
|
123
|
+
data = ai_mod.get_knowledge_base(session, kb_id)
|
|
124
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
125
|
+
except Exception as e:
|
|
126
|
+
ctx.handle_error(e)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@ai.command("knowledge-create")
|
|
130
|
+
@click.option("--config", "-c", required=True, help="Knowledge base config as JSON string")
|
|
131
|
+
@pass_context
|
|
132
|
+
def ai_knowledge_create(ctx, config):
|
|
133
|
+
"""Create or update a knowledge base (JSON config)."""
|
|
134
|
+
try:
|
|
135
|
+
session = ctx.get_session()
|
|
136
|
+
data = ai_mod.upsert_knowledge_base(session, json.loads(config))
|
|
137
|
+
ctx.output(data, lambda d: click.echo(f"Knowledge base created/updated: {d}"))
|
|
138
|
+
except json.JSONDecodeError as e:
|
|
139
|
+
ctx.handle_error(click.UsageError(f"Invalid JSON: {e}"))
|
|
140
|
+
except Exception as e:
|
|
141
|
+
ctx.handle_error(e)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@ai.command("knowledge-delete")
|
|
145
|
+
@click.argument("kb_id")
|
|
146
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
147
|
+
@pass_context
|
|
148
|
+
def ai_knowledge_delete(ctx, kb_id, yes):
|
|
149
|
+
"""Delete a knowledge base."""
|
|
150
|
+
try:
|
|
151
|
+
if not yes:
|
|
152
|
+
click.confirm(f"Delete knowledge base {kb_id}?", abort=True)
|
|
153
|
+
session = ctx.get_session()
|
|
154
|
+
data = ai_mod.delete_knowledge_base(session, kb_id)
|
|
155
|
+
ctx.output(data, lambda d: click.echo("Knowledge base deleted."))
|
|
156
|
+
except Exception as e:
|
|
157
|
+
ctx.handle_error(e)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# ── Chatbot ──────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@ai.command("chatbot-config")
|
|
164
|
+
@click.argument("process_id")
|
|
165
|
+
@pass_context
|
|
166
|
+
def ai_chatbot_config(ctx, process_id):
|
|
167
|
+
"""Get chatbot configuration for a workflow."""
|
|
168
|
+
try:
|
|
169
|
+
session = ctx.get_session()
|
|
170
|
+
data = ai_mod.get_chatbot_config(session, process_id)
|
|
171
|
+
ctx.output(data, lambda d: output_json(d))
|
|
172
|
+
except Exception as e:
|
|
173
|
+
ctx.handle_error(e)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@ai.command("chatbot-save")
|
|
177
|
+
@click.argument("process_id")
|
|
178
|
+
@click.option("--config", "-c", required=True, help="Chatbot config as JSON string")
|
|
179
|
+
@pass_context
|
|
180
|
+
def ai_chatbot_save(ctx, process_id, config):
|
|
181
|
+
"""Save chatbot configuration for a workflow."""
|
|
182
|
+
try:
|
|
183
|
+
session = ctx.get_session()
|
|
184
|
+
data = ai_mod.save_chatbot_config(session, process_id, json.loads(config))
|
|
185
|
+
ctx.output(data, lambda d: click.echo("Chatbot config saved."))
|
|
186
|
+
except json.JSONDecodeError as e:
|
|
187
|
+
ctx.handle_error(click.UsageError(f"Invalid JSON: {e}"))
|
|
188
|
+
except Exception as e:
|
|
189
|
+
ctx.handle_error(e)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ── AI Service ───────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@ai.command("service-status")
|
|
196
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
197
|
+
@pass_context
|
|
198
|
+
def ai_service_status(ctx, project_id):
|
|
199
|
+
"""Get AI service status."""
|
|
200
|
+
try:
|
|
201
|
+
session = ctx.get_session()
|
|
202
|
+
pid = project_id or session.default_project_id
|
|
203
|
+
if not pid:
|
|
204
|
+
raise click.UsageError("Project ID required.")
|
|
205
|
+
data = ai_mod.get_ai_service_status(session, pid)
|
|
206
|
+
ctx.output(data, lambda d: output_json(d))
|
|
207
|
+
except Exception as e:
|
|
208
|
+
ctx.handle_error(e)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@ai.command("models")
|
|
212
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
213
|
+
@pass_context
|
|
214
|
+
def ai_models(ctx, project_id):
|
|
215
|
+
"""List available AI models."""
|
|
216
|
+
try:
|
|
217
|
+
session = ctx.get_session()
|
|
218
|
+
pid = project_id or session.default_project_id
|
|
219
|
+
if not pid:
|
|
220
|
+
raise click.UsageError("Project ID required.")
|
|
221
|
+
data = ai_mod.get_models(session, pid)
|
|
222
|
+
ctx.output(data, lambda d: output_json(d))
|
|
223
|
+
except Exception as e:
|
|
224
|
+
ctx.handle_error(e)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""CLI commands for application management."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import app as app_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_table, output_kv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def app():
|
|
12
|
+
"""Application management."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command("list")
|
|
17
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
18
|
+
@pass_context
|
|
19
|
+
def app_list(ctx, project_id):
|
|
20
|
+
"""List applications in a project."""
|
|
21
|
+
try:
|
|
22
|
+
session = ctx.get_session()
|
|
23
|
+
apps = app_mod.get_all_home_apps(session, project_id)
|
|
24
|
+
ctx.output(
|
|
25
|
+
apps,
|
|
26
|
+
lambda d: output_table(
|
|
27
|
+
d,
|
|
28
|
+
["appId", "name", "orgName"],
|
|
29
|
+
["App ID", "Name", "Org Name"],
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
ctx.handle_error(e)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@app.command("list-managed")
|
|
37
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
38
|
+
@pass_context
|
|
39
|
+
def app_list_managed(ctx, project_id):
|
|
40
|
+
"""List applications where the current user is a manager."""
|
|
41
|
+
try:
|
|
42
|
+
session = ctx.get_session()
|
|
43
|
+
apps = app_mod.get_managed_apps(session, project_id)
|
|
44
|
+
ctx.output(
|
|
45
|
+
apps,
|
|
46
|
+
lambda d: output_table(
|
|
47
|
+
d,
|
|
48
|
+
["appId", "name", "worksheetCount"],
|
|
49
|
+
["App ID", "Name", "Worksheets"],
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
ctx.handle_error(e)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.command("info")
|
|
57
|
+
@click.argument("app_id", default="")
|
|
58
|
+
@pass_context
|
|
59
|
+
def app_info(ctx, app_id):
|
|
60
|
+
"""Get application information."""
|
|
61
|
+
try:
|
|
62
|
+
session = ctx.get_session()
|
|
63
|
+
aid = app_id or session.default_app_id
|
|
64
|
+
if not aid:
|
|
65
|
+
raise click.UsageError("App ID required.")
|
|
66
|
+
data = app_mod.get_app_info(session, aid)
|
|
67
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
68
|
+
except Exception as e:
|
|
69
|
+
ctx.handle_error(e)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.command("worksheets")
|
|
73
|
+
@click.argument("app_id", default="")
|
|
74
|
+
@pass_context
|
|
75
|
+
def app_worksheets(ctx, app_id):
|
|
76
|
+
"""List worksheets in an application."""
|
|
77
|
+
try:
|
|
78
|
+
session = ctx.get_session()
|
|
79
|
+
aid = app_id or session.default_app_id
|
|
80
|
+
if not aid:
|
|
81
|
+
raise click.UsageError("App ID required.")
|
|
82
|
+
sheets = app_mod.get_app_worksheets(session, aid)
|
|
83
|
+
ctx.output(
|
|
84
|
+
sheets,
|
|
85
|
+
lambda d: output_table(
|
|
86
|
+
d,
|
|
87
|
+
["workSheetId", "workSheetName"],
|
|
88
|
+
["Worksheet ID", "Name"],
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
except Exception as e:
|
|
92
|
+
ctx.handle_error(e)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ── App Lifecycle ────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.command("create")
|
|
99
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
100
|
+
@click.option("--name", "-n", required=True, help="Application name")
|
|
101
|
+
@click.option("--icon-color", default="", help="Icon color hex")
|
|
102
|
+
@pass_context
|
|
103
|
+
def app_create(ctx, project_id, name, icon_color):
|
|
104
|
+
"""Create a new application."""
|
|
105
|
+
try:
|
|
106
|
+
session = ctx.get_session()
|
|
107
|
+
data = app_mod.create_app(session, project_id, name, icon_color=icon_color)
|
|
108
|
+
ctx.output(data, lambda d: click.echo(f"Created app: {d}"))
|
|
109
|
+
except Exception as e:
|
|
110
|
+
ctx.handle_error(e)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.command("update")
|
|
114
|
+
@click.argument("app_id")
|
|
115
|
+
@click.option("--name", "-n", default="", help="New name")
|
|
116
|
+
@click.option("--desc", "-d", default="", help="New description")
|
|
117
|
+
@click.option("--icon-color", default="", help="Icon color hex")
|
|
118
|
+
@pass_context
|
|
119
|
+
def app_update(ctx, app_id, name, desc, icon_color):
|
|
120
|
+
"""Update application information."""
|
|
121
|
+
try:
|
|
122
|
+
session = ctx.get_session()
|
|
123
|
+
data = app_mod.update_app(session, app_id, name=name, description=desc, icon_color=icon_color)
|
|
124
|
+
ctx.output(data, lambda d: click.echo("App updated."))
|
|
125
|
+
except Exception as e:
|
|
126
|
+
ctx.handle_error(e)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.command("delete")
|
|
130
|
+
@click.argument("app_id")
|
|
131
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
132
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
133
|
+
@pass_context
|
|
134
|
+
def app_delete(ctx, app_id, project_id, yes):
|
|
135
|
+
"""Delete an application."""
|
|
136
|
+
try:
|
|
137
|
+
if not yes:
|
|
138
|
+
click.confirm(f"Delete app {app_id}?", abort=True)
|
|
139
|
+
session = ctx.get_session()
|
|
140
|
+
data = app_mod.delete_app(session, app_id, project_id)
|
|
141
|
+
ctx.output(data, lambda d: click.echo("App deleted."))
|
|
142
|
+
except Exception as e:
|
|
143
|
+
ctx.handle_error(e)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ── Section management ───────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.command("add-section")
|
|
150
|
+
@click.argument("app_id")
|
|
151
|
+
@click.option("--name", "-n", default="", help="Section name")
|
|
152
|
+
@pass_context
|
|
153
|
+
def app_add_section(ctx, app_id, name):
|
|
154
|
+
"""Add a section/group to an application."""
|
|
155
|
+
try:
|
|
156
|
+
session = ctx.get_session()
|
|
157
|
+
data = app_mod.add_section(session, app_id, name)
|
|
158
|
+
ctx.output(data, lambda d: click.echo(f"Section added: {d}"))
|
|
159
|
+
except Exception as e:
|
|
160
|
+
ctx.handle_error(e)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@app.command("edit-section")
|
|
164
|
+
@click.argument("app_id")
|
|
165
|
+
@click.argument("section_id")
|
|
166
|
+
@click.option("--name", "-n", required=True, help="New section name")
|
|
167
|
+
@pass_context
|
|
168
|
+
def app_edit_section(ctx, app_id, section_id, name):
|
|
169
|
+
"""Edit a section name."""
|
|
170
|
+
try:
|
|
171
|
+
session = ctx.get_session()
|
|
172
|
+
data = app_mod.edit_section(session, app_id, section_id, name)
|
|
173
|
+
ctx.output(data, lambda d: click.echo("Section updated."))
|
|
174
|
+
except Exception as e:
|
|
175
|
+
ctx.handle_error(e)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@app.command("delete-section")
|
|
179
|
+
@click.argument("app_id")
|
|
180
|
+
@click.argument("section_id")
|
|
181
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
182
|
+
@pass_context
|
|
183
|
+
def app_delete_section(ctx, app_id, section_id, yes):
|
|
184
|
+
"""Delete a section from an application."""
|
|
185
|
+
try:
|
|
186
|
+
if not yes:
|
|
187
|
+
click.confirm(f"Delete section {section_id}?", abort=True)
|
|
188
|
+
session = ctx.get_session()
|
|
189
|
+
data = app_mod.delete_section(session, app_id, section_id)
|
|
190
|
+
ctx.output(data, lambda d: click.echo("Section deleted."))
|
|
191
|
+
except Exception as e:
|
|
192
|
+
ctx.handle_error(e)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# ── Backup / Export ──────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@app.command("backup")
|
|
199
|
+
@click.argument("app_id")
|
|
200
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
201
|
+
@click.option("--no-data", is_flag=True, help="Backup without data")
|
|
202
|
+
@pass_context
|
|
203
|
+
def app_backup(ctx, app_id, project_id, no_data):
|
|
204
|
+
"""Backup an application."""
|
|
205
|
+
try:
|
|
206
|
+
session = ctx.get_session()
|
|
207
|
+
data = app_mod.backup_app(session, app_id, project_id, contain_data=not no_data)
|
|
208
|
+
ctx.output(data, lambda d: click.echo(f"Backup started: {d}"))
|
|
209
|
+
except Exception as e:
|
|
210
|
+
ctx.handle_error(e)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@app.command("restore")
|
|
214
|
+
@click.argument("app_id")
|
|
215
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
216
|
+
@click.option("--backup-id", "-b", required=True, help="Backup ID")
|
|
217
|
+
@click.option("--file-url", required=True, help="Backup file URL")
|
|
218
|
+
@click.option("--file-name", required=True, help="Backup file name")
|
|
219
|
+
@click.option("--no-data", is_flag=True, help="Restore without data")
|
|
220
|
+
@click.option("--as-new", is_flag=True, help="Restore as a new app")
|
|
221
|
+
@pass_context
|
|
222
|
+
def app_restore(ctx, app_id, project_id, backup_id, file_url, file_name, no_data, as_new):
|
|
223
|
+
"""Restore an application from backup."""
|
|
224
|
+
try:
|
|
225
|
+
session = ctx.get_session()
|
|
226
|
+
data = app_mod.restore_app(
|
|
227
|
+
session, app_id, project_id, backup_id,
|
|
228
|
+
file_url, file_name,
|
|
229
|
+
contain_data=not no_data, is_restore_new=as_new,
|
|
230
|
+
)
|
|
231
|
+
ctx.output(data, lambda d: click.echo(f"Restore started: {d}"))
|
|
232
|
+
except Exception as e:
|
|
233
|
+
ctx.handle_error(e)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@app.command("export")
|
|
237
|
+
@click.argument("app_ids", nargs=-1, required=True)
|
|
238
|
+
@pass_context
|
|
239
|
+
def app_export(ctx, app_ids):
|
|
240
|
+
"""Export applications (batch)."""
|
|
241
|
+
try:
|
|
242
|
+
session = ctx.get_session()
|
|
243
|
+
data = app_mod.export_apps(session, list(app_ids))
|
|
244
|
+
ctx.output(data, lambda d: click.echo(f"Export started: {d}"))
|
|
245
|
+
except Exception as e:
|
|
246
|
+
ctx.handle_error(e)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@app.command("exports")
|
|
250
|
+
@click.argument("app_id")
|
|
251
|
+
@pass_context
|
|
252
|
+
def app_exports(ctx, app_id):
|
|
253
|
+
"""List exports for an application."""
|
|
254
|
+
try:
|
|
255
|
+
session = ctx.get_session()
|
|
256
|
+
data = app_mod.get_exports(session, app_id)
|
|
257
|
+
ctx.output(data, lambda d: output_json(d))
|
|
258
|
+
except Exception as e:
|
|
259
|
+
ctx.handle_error(e)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@app.command("backup-logs")
|
|
263
|
+
@click.argument("app_id")
|
|
264
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
265
|
+
@click.option("--page-size", "-n", default=20, help="Items per page")
|
|
266
|
+
@click.option("--page", default=1, help="Page number")
|
|
267
|
+
@pass_context
|
|
268
|
+
def app_backup_logs(ctx, app_id, project_id, page_size, page):
|
|
269
|
+
"""Get backup/restore operation logs."""
|
|
270
|
+
try:
|
|
271
|
+
session = ctx.get_session()
|
|
272
|
+
data = app_mod.get_backup_logs(session, app_id, project_id, page, page_size)
|
|
273
|
+
ctx.output(data, lambda d: output_json(d))
|
|
274
|
+
except Exception as e:
|
|
275
|
+
ctx.handle_error(e)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# ── Usage & Logs ─────────────────────────────────────────────────────────
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@app.command("usage")
|
|
282
|
+
@click.option("--project-id", "-p", required=True, help="Project/org ID")
|
|
283
|
+
@click.option("--app-id", "-a", default="", help="Filter by app ID")
|
|
284
|
+
@click.option("--days", "-d", default=7, type=int, help="Day range (default 7)")
|
|
285
|
+
@pass_context
|
|
286
|
+
def app_usage(ctx, project_id, app_id, days):
|
|
287
|
+
"""Get application usage statistics."""
|
|
288
|
+
try:
|
|
289
|
+
session = ctx.get_session()
|
|
290
|
+
data = app_mod.get_usage_stats(session, project_id, app_id=app_id, day_range=days)
|
|
291
|
+
ctx.output(data, lambda d: output_json(d))
|
|
292
|
+
except Exception as e:
|
|
293
|
+
ctx.handle_error(e)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@app.command("logs")
|
|
297
|
+
@click.argument("app_id")
|
|
298
|
+
@click.option("--page-size", "-n", default=50, help="Items per page")
|
|
299
|
+
@click.option("--page", default=1, help="Page number")
|
|
300
|
+
@pass_context
|
|
301
|
+
def app_logs(ctx, app_id, page_size, page):
|
|
302
|
+
"""Get application operation logs."""
|
|
303
|
+
try:
|
|
304
|
+
session = ctx.get_session()
|
|
305
|
+
data = app_mod.get_app_logs(session, app_id, page, page_size)
|
|
306
|
+
ctx.output(data, lambda d: output_json(d))
|
|
307
|
+
except Exception as e:
|
|
308
|
+
ctx.handle_error(e)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""CLI commands for calendar/schedule management."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import calendar_mod as cal_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_kv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def calendar():
|
|
12
|
+
"""Calendar and schedule management."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@calendar.command("list")
|
|
17
|
+
@click.option("--start-date", "-s", required=True, help="Start date (YYYY-MM-DD)")
|
|
18
|
+
@click.option("--end-date", "-e", required=True, help="End date (YYYY-MM-DD)")
|
|
19
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
20
|
+
@pass_context
|
|
21
|
+
def calendar_list(ctx, start_date, end_date, project_id):
|
|
22
|
+
"""List calendar events in a date range."""
|
|
23
|
+
try:
|
|
24
|
+
session = ctx.get_session()
|
|
25
|
+
pid = project_id or session.default_project_id
|
|
26
|
+
data = cal_mod.get_calendars(session, start_date, end_date, project_id=pid)
|
|
27
|
+
ctx.output(data, lambda d: output_json(d))
|
|
28
|
+
except Exception as e:
|
|
29
|
+
ctx.handle_error(e)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@calendar.command("get")
|
|
33
|
+
@click.argument("calendar_id")
|
|
34
|
+
@pass_context
|
|
35
|
+
def calendar_get(ctx, calendar_id):
|
|
36
|
+
"""Get calendar event detail."""
|
|
37
|
+
try:
|
|
38
|
+
session = ctx.get_session()
|
|
39
|
+
data = cal_mod.get_calendar_detail(session, calendar_id)
|
|
40
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
41
|
+
except Exception as e:
|
|
42
|
+
ctx.handle_error(e)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@calendar.command("create")
|
|
46
|
+
@click.option("--name", "-n", required=True, help="Event title")
|
|
47
|
+
@click.option("--start-date", "-s", required=True, help="Start date/time")
|
|
48
|
+
@click.option("--end-date", "-e", required=True, help="End date/time")
|
|
49
|
+
@click.option("--desc", "-d", default="", help="Description")
|
|
50
|
+
@click.option("--private", is_flag=True, help="Make event private")
|
|
51
|
+
@pass_context
|
|
52
|
+
def calendar_create(ctx, name, start_date, end_date, desc, private):
|
|
53
|
+
"""Create a new calendar event."""
|
|
54
|
+
try:
|
|
55
|
+
session = ctx.get_session()
|
|
56
|
+
data = cal_mod.insert_calendar(
|
|
57
|
+
session, name, start_date, end_date,
|
|
58
|
+
description=desc, is_private=private,
|
|
59
|
+
)
|
|
60
|
+
ctx.output(data, lambda d: click.echo(f"Event created: {d}"))
|
|
61
|
+
except Exception as e:
|
|
62
|
+
ctx.handle_error(e)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@calendar.command("update")
|
|
66
|
+
@click.argument("calendar_id")
|
|
67
|
+
@click.option("--name", "-n", default="", help="New title")
|
|
68
|
+
@click.option("--start-date", "-s", default="", help="New start date/time")
|
|
69
|
+
@click.option("--end-date", "-e", default="", help="New end date/time")
|
|
70
|
+
@click.option("--desc", "-d", default="", help="New description")
|
|
71
|
+
@pass_context
|
|
72
|
+
def calendar_update(ctx, calendar_id, name, start_date, end_date, desc):
|
|
73
|
+
"""Update a calendar event."""
|
|
74
|
+
try:
|
|
75
|
+
session = ctx.get_session()
|
|
76
|
+
data = cal_mod.edit_calendar(
|
|
77
|
+
session, calendar_id, name=name,
|
|
78
|
+
start_date=start_date, end_date=end_date, description=desc,
|
|
79
|
+
)
|
|
80
|
+
ctx.output(data, lambda d: click.echo("Event updated."))
|
|
81
|
+
except Exception as e:
|
|
82
|
+
ctx.handle_error(e)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@calendar.command("delete")
|
|
86
|
+
@click.argument("calendar_id")
|
|
87
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
88
|
+
@pass_context
|
|
89
|
+
def calendar_delete(ctx, calendar_id, yes):
|
|
90
|
+
"""Delete a calendar event."""
|
|
91
|
+
try:
|
|
92
|
+
if not yes:
|
|
93
|
+
click.confirm(f"Delete event {calendar_id}?", abort=True)
|
|
94
|
+
session = ctx.get_session()
|
|
95
|
+
data = cal_mod.delete_calendar(session, calendar_id)
|
|
96
|
+
ctx.output(data, lambda d: click.echo("Event deleted."))
|
|
97
|
+
except Exception as e:
|
|
98
|
+
ctx.handle_error(e)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@calendar.command("add-member")
|
|
102
|
+
@click.argument("calendar_id")
|
|
103
|
+
@click.option("--user", "-u", multiple=True, required=True, help="User account ID to invite")
|
|
104
|
+
@pass_context
|
|
105
|
+
def calendar_add_member(ctx, calendar_id, user):
|
|
106
|
+
"""Add members to a calendar event."""
|
|
107
|
+
try:
|
|
108
|
+
session = ctx.get_session()
|
|
109
|
+
data = cal_mod.add_members(session, calendar_id, list(user))
|
|
110
|
+
ctx.output(data, lambda d: click.echo("Members added."))
|
|
111
|
+
except Exception as e:
|
|
112
|
+
ctx.handle_error(e)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@calendar.command("remove-member")
|
|
116
|
+
@click.argument("calendar_id")
|
|
117
|
+
@click.argument("account_id")
|
|
118
|
+
@pass_context
|
|
119
|
+
def calendar_remove_member(ctx, calendar_id, account_id):
|
|
120
|
+
"""Remove a member from a calendar event."""
|
|
121
|
+
try:
|
|
122
|
+
session = ctx.get_session()
|
|
123
|
+
data = cal_mod.remove_member(session, calendar_id, account_id)
|
|
124
|
+
ctx.output(data, lambda d: click.echo("Member removed."))
|
|
125
|
+
except Exception as e:
|
|
126
|
+
ctx.handle_error(e)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@calendar.command("categories")
|
|
130
|
+
@pass_context
|
|
131
|
+
def calendar_categories(ctx):
|
|
132
|
+
"""List calendar categories."""
|
|
133
|
+
try:
|
|
134
|
+
session = ctx.get_session()
|
|
135
|
+
data = cal_mod.get_categories(session)
|
|
136
|
+
ctx.output(data, lambda d: output_json(d))
|
|
137
|
+
except Exception as e:
|
|
138
|
+
ctx.handle_error(e)
|