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.
Files changed (58) hide show
  1. hap_cli/README.md +194 -0
  2. hap_cli/README_CN.md +601 -0
  3. hap_cli/__init__.py +3 -0
  4. hap_cli/commands/__init__.py +1 -0
  5. hap_cli/commands/ai_cmd.py +224 -0
  6. hap_cli/commands/app_cmd.py +308 -0
  7. hap_cli/commands/calendar_cmd.py +138 -0
  8. hap_cli/commands/chat_cmd.py +101 -0
  9. hap_cli/commands/config_cmd.py +169 -0
  10. hap_cli/commands/contact_cmd.py +125 -0
  11. hap_cli/commands/department_cmd.py +168 -0
  12. hap_cli/commands/group_cmd.py +128 -0
  13. hap_cli/commands/instance_cmd.py +310 -0
  14. hap_cli/commands/node_cmd.py +538 -0
  15. hap_cli/commands/optionset_cmd.py +99 -0
  16. hap_cli/commands/page_cmd.py +102 -0
  17. hap_cli/commands/plugin_cmd.py +133 -0
  18. hap_cli/commands/post_cmd.py +155 -0
  19. hap_cli/commands/record_cmd.py +228 -0
  20. hap_cli/commands/role_cmd.py +221 -0
  21. hap_cli/commands/workflow_cmd.py +284 -0
  22. hap_cli/commands/worksheet_cmd.py +342 -0
  23. hap_cli/context.py +43 -0
  24. hap_cli/core/__init__.py +1 -0
  25. hap_cli/core/ai.py +133 -0
  26. hap_cli/core/app.py +307 -0
  27. hap_cli/core/auth.py +219 -0
  28. hap_cli/core/calendar_mod.py +114 -0
  29. hap_cli/core/chat.py +73 -0
  30. hap_cli/core/contact.py +85 -0
  31. hap_cli/core/department.py +131 -0
  32. hap_cli/core/flow_node.py +1001 -0
  33. hap_cli/core/group.py +99 -0
  34. hap_cli/core/instance.py +572 -0
  35. hap_cli/core/optionset.py +112 -0
  36. hap_cli/core/page.py +138 -0
  37. hap_cli/core/plugin.py +87 -0
  38. hap_cli/core/post.py +118 -0
  39. hap_cli/core/record.py +268 -0
  40. hap_cli/core/role.py +227 -0
  41. hap_cli/core/session.py +348 -0
  42. hap_cli/core/workflow.py +556 -0
  43. hap_cli/core/worksheet.py +403 -0
  44. hap_cli/hap_cli.py +105 -0
  45. hap_cli/skills/SKILL.md +383 -0
  46. hap_cli/skills/__init__.py +0 -0
  47. hap_cli/tests/__init__.py +1 -0
  48. hap_cli/tests/test_core.py +1824 -0
  49. hap_cli/tests/test_full_e2e.py +136 -0
  50. hap_cli/tests/test_integration.py +805 -0
  51. hap_cli/utils/__init__.py +1 -0
  52. hap_cli/utils/formatting.py +111 -0
  53. hap_cli/utils/options.py +10 -0
  54. hap_cli-0.5.0.dist-info/METADATA +223 -0
  55. hap_cli-0.5.0.dist-info/RECORD +58 -0
  56. hap_cli-0.5.0.dist-info/WHEEL +5 -0
  57. hap_cli-0.5.0.dist-info/entry_points.txt +2 -0
  58. hap_cli-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,342 @@
1
+ """CLI commands for worksheet operations."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from hap_cli.context import pass_context
8
+ from hap_cli.core import worksheet as ws_mod
9
+ from hap_cli.utils.formatting import output_json, output_table, output_kv
10
+
11
+
12
+ @click.group()
13
+ def worksheet():
14
+ """Worksheet operations."""
15
+ pass
16
+
17
+
18
+ @worksheet.command("info")
19
+ @click.argument("worksheet_id")
20
+ @click.option("--app-id", "-a", default="", help="Application ID")
21
+ @pass_context
22
+ def worksheet_info(ctx, worksheet_id, app_id):
23
+ """Get worksheet information."""
24
+ try:
25
+ session = ctx.get_session()
26
+ data = ws_mod.get_worksheet_info(session, worksheet_id, app_id)
27
+ ctx.output(data, lambda d: output_kv(d))
28
+ except Exception as e:
29
+ ctx.handle_error(e)
30
+
31
+
32
+ @worksheet.command("fields")
33
+ @click.argument("worksheet_id")
34
+ @click.option("--app-id", "-a", default="", help="Application ID")
35
+ @pass_context
36
+ def worksheet_fields(ctx, worksheet_id, app_id):
37
+ """List worksheet fields/controls."""
38
+ try:
39
+ session = ctx.get_session()
40
+ controls = ws_mod.get_worksheet_controls(session, worksheet_id, app_id)
41
+ ctx.output(
42
+ controls,
43
+ lambda d: output_table(
44
+ d,
45
+ ["controlId", "controlName", "type", "required"],
46
+ ["Control ID", "Name", "Type", "Required"],
47
+ ),
48
+ )
49
+ except Exception as e:
50
+ ctx.handle_error(e)
51
+
52
+
53
+ @worksheet.command("views")
54
+ @click.argument("worksheet_id")
55
+ @click.option("--app-id", "-a", default="", help="Application ID")
56
+ @pass_context
57
+ def worksheet_views(ctx, worksheet_id, app_id):
58
+ """List worksheet views."""
59
+ try:
60
+ session = ctx.get_session()
61
+ views = ws_mod.get_worksheet_views(session, worksheet_id, app_id)
62
+ ctx.output(
63
+ views,
64
+ lambda d: output_table(
65
+ d,
66
+ ["viewId", "name", "viewType"],
67
+ ["View ID", "Name", "Type"],
68
+ ),
69
+ )
70
+ except Exception as e:
71
+ ctx.handle_error(e)
72
+
73
+
74
+ # ── Worksheet CRUD ───────────────────────────────────────────────────────
75
+
76
+
77
+ @worksheet.command("create")
78
+ @click.argument("app_id")
79
+ @click.argument("name")
80
+ @click.option("--project-id", default="", help="Project ID")
81
+ @click.option("--section-id", default="", help="Section ID")
82
+ @pass_context
83
+ def worksheet_create(ctx, app_id, name, project_id, section_id):
84
+ """Create a new worksheet."""
85
+ try:
86
+ session = ctx.get_session()
87
+ data = ws_mod.create_worksheet(session, app_id, name, project_id, section_id)
88
+ ctx.output(data, lambda d: output_json(d))
89
+ except Exception as e:
90
+ ctx.handle_error(e)
91
+
92
+
93
+ @worksheet.command("copy")
94
+ @click.argument("worksheet_id")
95
+ @click.argument("name")
96
+ @click.option("--app-id", "-a", default="", help="Application ID")
97
+ @click.option("--project-id", default="", help="Project ID")
98
+ @click.option("--copy-rows", is_flag=True, default=False, help="Copy rows as well")
99
+ @pass_context
100
+ def worksheet_copy(ctx, worksheet_id, name, app_id, project_id, copy_rows):
101
+ """Copy a worksheet."""
102
+ try:
103
+ session = ctx.get_session()
104
+ data = ws_mod.copy_worksheet(session, worksheet_id, name, app_id, project_id, copy_rows)
105
+ ctx.output(data, lambda d: output_json(d))
106
+ except Exception as e:
107
+ ctx.handle_error(e)
108
+
109
+
110
+ @worksheet.command("update")
111
+ @click.argument("worksheet_id")
112
+ @click.option("--alias", default="", help="New alias for the worksheet")
113
+ @click.option("--desc", default="", help="New description for the worksheet")
114
+ @click.option("--app-id", "-a", default="", help="Application ID")
115
+ @pass_context
116
+ def worksheet_update(ctx, worksheet_id, alias, desc, app_id):
117
+ """Update worksheet alias and/or description."""
118
+ try:
119
+ session = ctx.get_session()
120
+ data = ws_mod.update_worksheet(session, worksheet_id, alias, desc, app_id)
121
+ ctx.output(data, lambda d: output_json(d))
122
+ except Exception as e:
123
+ ctx.handle_error(e)
124
+
125
+
126
+ # ── Field type reference ─────────────────────────────────────────────────
127
+
128
+
129
+ @worksheet.command("field-types")
130
+ @pass_context
131
+ def worksheet_field_types(ctx):
132
+ """Show field type constants as a reference table."""
133
+ try:
134
+ rows = [{"name": k, "type_id": v} for k, v in ws_mod.FIELD_TYPES.items()]
135
+ ctx.output(
136
+ rows,
137
+ lambda d: output_table(d, ["name", "type_id"], ["Field Type", "Type ID"]),
138
+ )
139
+ except Exception as e:
140
+ ctx.handle_error(e)
141
+
142
+
143
+ # ── Field/Control management ─────────────────────────────────────────────
144
+
145
+
146
+ @worksheet.command("save-fields")
147
+ @click.argument("worksheet_id")
148
+ @click.option("--controls", required=True, help="JSON array of the complete controls list (get current list via 'fields' command first)")
149
+ @pass_context
150
+ def worksheet_save_fields(ctx, worksheet_id, controls):
151
+ """Save the complete fields/controls list for a worksheet.
152
+
153
+ This replaces add-field/edit-field/delete-field with a single full-replacement
154
+ approach: fetch current fields via 'fields' command, modify the list as needed
155
+ (add new entries, update existing ones, or remove entries), then pass the
156
+ complete modified list here.
157
+
158
+ Example workflow:
159
+ hap worksheet fields <worksheet_id> --json > controls.json
160
+ # edit controls.json as needed
161
+ hap worksheet save-fields <worksheet_id> --controls "$(cat controls.json)"
162
+ """
163
+ try:
164
+ session = ctx.get_session()
165
+ controls_data = json.loads(controls)
166
+ data = ws_mod.save_controls(session, worksheet_id, controls_data)
167
+ ctx.output(data, lambda d: output_json(d))
168
+ except Exception as e:
169
+ ctx.handle_error(e)
170
+
171
+
172
+ # ── View management ──────────────────────────────────────────────────────
173
+
174
+
175
+ @worksheet.command("add-view")
176
+ @click.argument("worksheet_id")
177
+ @click.argument("name")
178
+ @click.option("--view-type", type=int, default=0, help="View type (default: 0)")
179
+ @click.option("--app-id", "-a", default="", help="Application ID")
180
+ @pass_context
181
+ def worksheet_add_view(ctx, worksheet_id, name, view_type, app_id):
182
+ """Create a new worksheet view."""
183
+ try:
184
+ session = ctx.get_session()
185
+ data = ws_mod.save_view(session, worksheet_id, name, view_type, app_id=app_id)
186
+ ctx.output(data, lambda d: output_json(d))
187
+ except Exception as e:
188
+ ctx.handle_error(e)
189
+
190
+
191
+ @worksheet.command("delete-view")
192
+ @click.argument("worksheet_id")
193
+ @click.argument("view_id")
194
+ @click.option("--app-id", "-a", default="", help="Application ID")
195
+ @pass_context
196
+ def worksheet_delete_view(ctx, worksheet_id, view_id, app_id):
197
+ """Delete a worksheet view."""
198
+ try:
199
+ session = ctx.get_session()
200
+ data = ws_mod.delete_view(session, worksheet_id, view_id, app_id)
201
+ ctx.output(data, lambda d: output_json(d))
202
+ except Exception as e:
203
+ ctx.handle_error(e)
204
+
205
+
206
+ @worksheet.command("copy-view")
207
+ @click.argument("worksheet_id")
208
+ @click.argument("view_id")
209
+ @click.argument("name")
210
+ @pass_context
211
+ def worksheet_copy_view(ctx, worksheet_id, view_id, name):
212
+ """Copy a worksheet view."""
213
+ try:
214
+ session = ctx.get_session()
215
+ data = ws_mod.copy_view(session, worksheet_id, view_id, name)
216
+ ctx.output(data, lambda d: output_json(d))
217
+ except Exception as e:
218
+ ctx.handle_error(e)
219
+
220
+
221
+ @worksheet.command("sort-views")
222
+ @click.argument("worksheet_id")
223
+ @click.argument("view_ids", nargs=-1, required=True)
224
+ @pass_context
225
+ def worksheet_sort_views(ctx, worksheet_id, view_ids):
226
+ """Reorder worksheet views. Pass view IDs in desired order."""
227
+ try:
228
+ session = ctx.get_session()
229
+ data = ws_mod.sort_views(session, worksheet_id, list(view_ids))
230
+ ctx.output(data, lambda d: output_json(d))
231
+ except Exception as e:
232
+ ctx.handle_error(e)
233
+
234
+
235
+ # ── Business Rules ───────────────────────────────────────────────────────
236
+
237
+
238
+ @worksheet.command("rules")
239
+ @click.argument("worksheet_id")
240
+ @click.option("--control-id", default="", help="Filter by control ID")
241
+ @pass_context
242
+ def worksheet_rules(ctx, worksheet_id, control_id):
243
+ """Get business rules for worksheet controls."""
244
+ try:
245
+ session = ctx.get_session()
246
+ data = ws_mod.get_control_rules(session, worksheet_id, control_id)
247
+ ctx.output(data, lambda d: output_json(d))
248
+ except Exception as e:
249
+ ctx.handle_error(e)
250
+
251
+
252
+ @worksheet.command("save-rule")
253
+ @click.argument("worksheet_id")
254
+ @click.option("--rules", required=True, help="JSON array of rule definitions")
255
+ @click.option("--control-id", default="", help="Control ID to associate rules with")
256
+ @pass_context
257
+ def worksheet_save_rule(ctx, worksheet_id, rules, control_id):
258
+ """Save business rules for worksheet controls."""
259
+ try:
260
+ session = ctx.get_session()
261
+ rules_data = json.loads(rules)
262
+ data = ws_mod.save_control_rule(session, worksheet_id, rules_data, control_id)
263
+ ctx.output(data, lambda d: output_json(d))
264
+ except Exception as e:
265
+ ctx.handle_error(e)
266
+
267
+
268
+ # ── Custom Buttons ───────────────────────────────────────────────────────
269
+
270
+
271
+ @worksheet.command("buttons")
272
+ @click.argument("worksheet_id")
273
+ @pass_context
274
+ def worksheet_buttons(ctx, worksheet_id):
275
+ """Get custom action buttons for a worksheet."""
276
+ try:
277
+ session = ctx.get_session()
278
+ data = ws_mod.get_buttons(session, worksheet_id)
279
+ ctx.output(data, lambda d: output_json(d))
280
+ except Exception as e:
281
+ ctx.handle_error(e)
282
+
283
+
284
+ @worksheet.command("save-button")
285
+ @click.argument("worksheet_id")
286
+ @click.option("--config", "btn_config", required=True, help="JSON object of button configuration")
287
+ @click.option("--btn-id", default="", help="Button ID (for updating existing button)")
288
+ @pass_context
289
+ def worksheet_save_button(ctx, worksheet_id, btn_config, btn_id):
290
+ """Create or update a custom action button."""
291
+ try:
292
+ session = ctx.get_session()
293
+ btn_data = json.loads(btn_config)
294
+ data = ws_mod.save_button(session, worksheet_id, btn_data, btn_id)
295
+ ctx.output(data, lambda d: output_json(d))
296
+ except Exception as e:
297
+ ctx.handle_error(e)
298
+
299
+
300
+ @worksheet.command("delete-button")
301
+ @click.argument("worksheet_id")
302
+ @click.argument("btn_id")
303
+ @pass_context
304
+ def worksheet_delete_button(ctx, worksheet_id, btn_id):
305
+ """Delete a custom action button."""
306
+ try:
307
+ session = ctx.get_session()
308
+ data = ws_mod.delete_button(session, worksheet_id, btn_id)
309
+ ctx.output(data, lambda d: output_json(d))
310
+ except Exception as e:
311
+ ctx.handle_error(e)
312
+
313
+
314
+ # ── Feature Switches ────────────────────────────────────────────────────
315
+
316
+
317
+ @worksheet.command("switches")
318
+ @click.argument("worksheet_id")
319
+ @pass_context
320
+ def worksheet_switches(ctx, worksheet_id):
321
+ """Get feature switches for a worksheet."""
322
+ try:
323
+ session = ctx.get_session()
324
+ data = ws_mod.get_switches(session, worksheet_id)
325
+ ctx.output(data, lambda d: output_json(d))
326
+ except Exception as e:
327
+ ctx.handle_error(e)
328
+
329
+
330
+ @worksheet.command("edit-switch")
331
+ @click.argument("worksheet_id")
332
+ @click.argument("switch_id", type=int)
333
+ @click.argument("value", type=bool)
334
+ @pass_context
335
+ def worksheet_edit_switch(ctx, worksheet_id, switch_id, value):
336
+ """Edit a single feature switch."""
337
+ try:
338
+ session = ctx.get_session()
339
+ data = ws_mod.edit_switch(session, worksheet_id, switch_id, value)
340
+ ctx.output(data, lambda d: output_json(d))
341
+ except Exception as e:
342
+ ctx.handle_error(e)
hap_cli/context.py ADDED
@@ -0,0 +1,43 @@
1
+ """Shared CLI context for hap-cli."""
2
+
3
+ import sys
4
+ from typing import Optional
5
+
6
+ import click
7
+
8
+ from hap_cli.core.session import Session, SessionError, APIError
9
+ from hap_cli.utils.formatting import output_json
10
+
11
+
12
+ class Context:
13
+ """Shared CLI context."""
14
+
15
+ def __init__(self):
16
+ self.json_mode = False
17
+ self.session: Optional[Session] = None
18
+
19
+ def get_session(self) -> Session:
20
+ if self.session is None:
21
+ self.session = Session.load()
22
+ return self.session
23
+
24
+ def output(self, data, table_fn=None):
25
+ """Output data in JSON or human-readable format."""
26
+ if self.json_mode:
27
+ output_json(data)
28
+ elif table_fn:
29
+ table_fn(data)
30
+ else:
31
+ output_json(data)
32
+
33
+ def handle_error(self, e: Exception):
34
+ """Handle and display errors."""
35
+ msg = str(e)
36
+ if self.json_mode:
37
+ output_json({"error": msg, "type": type(e).__name__})
38
+ else:
39
+ click.echo(f"Error: {msg}", err=True)
40
+ sys.exit(1)
41
+
42
+
43
+ pass_context = click.make_pass_decorator(Context, ensure=True)
@@ -0,0 +1 @@
1
+ """Core modules for hap-cli harness."""
hap_cli/core/ai.py ADDED
@@ -0,0 +1,133 @@
1
+ """AI assistant and service management for MingDAO HAP."""
2
+
3
+ from typing import Any, Optional
4
+
5
+ from hap_cli.core.session import Session
6
+
7
+
8
+ # ── AI Assistant ─────────────────────────────────────────────────────────
9
+
10
+
11
+ def get_assistants(
12
+ session: Session,
13
+ project_id: str,
14
+ page_index: int = 1,
15
+ page_size: int = 20,
16
+ ) -> dict[str, Any]:
17
+ """Get AI assistant list."""
18
+ return session.api_call(
19
+ "Assistant", "GetList",
20
+ {"projectId": project_id, "pageIndex": page_index, "pageSize": page_size},
21
+ )
22
+
23
+
24
+ def get_assistant(
25
+ session: Session,
26
+ assistant_id: str,
27
+ ) -> dict[str, Any]:
28
+ """Get AI assistant detail."""
29
+ return session.api_call("Assistant", "Get", {"assistantId": assistant_id})
30
+
31
+
32
+ def upsert_assistant(
33
+ session: Session,
34
+ config: dict[str, Any],
35
+ ) -> dict[str, Any]:
36
+ """Create or update an AI assistant."""
37
+ return session.api_call("Assistant", "Upsert", config)
38
+
39
+
40
+ def delete_assistant(
41
+ session: Session,
42
+ assistant_id: str,
43
+ ) -> dict[str, Any]:
44
+ """Delete an AI assistant."""
45
+ return session.api_call("Assistant", "Delete", {"assistantId": assistant_id})
46
+
47
+
48
+ def set_assistant_status(
49
+ session: Session,
50
+ assistant_id: str,
51
+ status: int,
52
+ ) -> dict[str, Any]:
53
+ """Enable or disable an AI assistant."""
54
+ return session.api_call(
55
+ "Assistant", "SetStatus",
56
+ {"assistantId": assistant_id, "status": status},
57
+ )
58
+
59
+
60
+ # ── Knowledge Base ───────────────────────────────────────────────────────
61
+
62
+
63
+ def get_knowledge_bases(
64
+ session: Session,
65
+ project_id: str,
66
+ ) -> dict[str, Any]:
67
+ """Get knowledge base list."""
68
+ return session.api_call("Assistant", "GetListKnowledgeBase", {"projectId": project_id})
69
+
70
+
71
+ def get_knowledge_base(
72
+ session: Session,
73
+ knowledge_base_id: str,
74
+ ) -> dict[str, Any]:
75
+ """Get knowledge base detail."""
76
+ return session.api_call("Assistant", "GetKnowledgeBase", {"knowledgeBaseId": knowledge_base_id})
77
+
78
+
79
+ def upsert_knowledge_base(
80
+ session: Session,
81
+ config: dict[str, Any],
82
+ ) -> dict[str, Any]:
83
+ """Create or update a knowledge base."""
84
+ return session.api_call("Assistant", "UpsertKnowledgeBase", config)
85
+
86
+
87
+ def delete_knowledge_base(
88
+ session: Session,
89
+ knowledge_base_id: str,
90
+ ) -> dict[str, Any]:
91
+ """Delete a knowledge base."""
92
+ return session.api_call("Assistant", "DeleteKnowledgeBase", {"knowledgeBaseId": knowledge_base_id})
93
+
94
+
95
+ # ── Chatbot Configuration ────────────────────────────────────────────────
96
+
97
+
98
+ def get_chatbot_config(
99
+ session: Session,
100
+ process_id: str,
101
+ ) -> dict[str, Any]:
102
+ """Get chatbot configuration for a process/workflow."""
103
+ return session.workflow_call("getChatbotConfig", {"processId": process_id})
104
+
105
+
106
+ def save_chatbot_config(
107
+ session: Session,
108
+ process_id: str,
109
+ config: dict[str, Any],
110
+ ) -> dict[str, Any]:
111
+ """Save chatbot configuration for a process/workflow."""
112
+ data: dict[str, Any] = {"processId": process_id}
113
+ data.update(config)
114
+ return session.workflow_call("saveChatbotConfig", data)
115
+
116
+
117
+ # ── AI Service ───────────────────────────────────────────────────────────
118
+
119
+
120
+ def get_ai_service_status(
121
+ session: Session,
122
+ project_id: str,
123
+ ) -> dict[str, Any]:
124
+ """Get AI service enable/disable status."""
125
+ return session.api_call("AIService", "GetAIServiceStatus", {"projectId": project_id})
126
+
127
+
128
+ def get_models(
129
+ session: Session,
130
+ project_id: str,
131
+ ) -> dict[str, Any]:
132
+ """Get available AI models with developer info."""
133
+ return session.api_call("AIService", "GetDeveloperWithModes", {"projectId": project_id})