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,403 @@
1
+ """Worksheet management module for MingDAO HAP."""
2
+
3
+ from typing import Any, Optional
4
+
5
+ from hap_cli.core.session import Session
6
+
7
+
8
+ def get_worksheet_info(
9
+ session: Session,
10
+ worksheet_id: str,
11
+ app_id: str = "",
12
+ get_template: bool = False,
13
+ get_views: bool = False,
14
+ ) -> dict[str, Any]:
15
+ """Get worksheet information.
16
+
17
+ Args:
18
+ session: Active session
19
+ worksheet_id: Worksheet ID
20
+ app_id: Optional application ID
21
+ get_template: Include template controls
22
+ get_views: Include views
23
+
24
+ Returns:
25
+ Worksheet info dict
26
+ """
27
+ data = {
28
+ "worksheetId": worksheet_id,
29
+ "getTemplate": get_template,
30
+ "getViews": get_views,
31
+ }
32
+ if app_id:
33
+ data["appId"] = app_id
34
+ return session.api_call("Worksheet", "GetWorksheetInfo", data)
35
+
36
+
37
+ def get_worksheet_controls(
38
+ session: Session,
39
+ worksheet_id: str,
40
+ app_id: str = "",
41
+ ) -> list[dict[str, Any]]:
42
+ """Get worksheet fields/controls with full metadata (type, options, etc.).
43
+
44
+ Args:
45
+ session: Active session
46
+ worksheet_id: Worksheet ID
47
+ app_id: Optional application ID
48
+
49
+ Returns:
50
+ List of control/field definitions
51
+ """
52
+ data = {
53
+ "worksheetId": worksheet_id,
54
+ "getRelationSearch": True,
55
+ "resultType": 3, # Return full control metadata
56
+ }
57
+ if app_id:
58
+ data["appId"] = app_id
59
+ result = session.api_call("Worksheet", "GetWorksheetControls", data)
60
+ if isinstance(result, list):
61
+ return result
62
+ # Response is double-nested: {code, data: {sourceId, controls: [...]}}
63
+ if isinstance(result, dict):
64
+ inner = result.get("data")
65
+ if isinstance(inner, dict):
66
+ return inner.get("controls", [])
67
+ if isinstance(inner, list):
68
+ return inner
69
+ return result.get("controls", [])
70
+
71
+
72
+ def get_worksheet_views(
73
+ session: Session,
74
+ worksheet_id: str,
75
+ app_id: str = "",
76
+ ) -> list[dict[str, Any]]:
77
+ """Get worksheet views.
78
+
79
+ Args:
80
+ session: Active session
81
+ worksheet_id: Worksheet ID
82
+ app_id: Optional application ID
83
+
84
+ Returns:
85
+ List of view definitions
86
+ """
87
+ data = {"worksheetId": worksheet_id}
88
+ if app_id:
89
+ data["appId"] = app_id
90
+ result = session.api_call("Worksheet", "GetWorksheetViews", data)
91
+ if isinstance(result, list):
92
+ return result
93
+ return result.get("views", [])
94
+
95
+
96
+ # ── Field type constants ─────────────────────────────────────────────────
97
+
98
+ FIELD_TYPES = {
99
+ "TEXT": 2,
100
+ "MOBILE_PHONE": 3,
101
+ "TELEPHONE": 4,
102
+ "EMAIL": 5,
103
+ "NUMBER": 6,
104
+ "CRED": 7,
105
+ "MONEY": 8,
106
+ "FLAT_MENU": 9,
107
+ "MULTI_SELECT": 10,
108
+ "DROP_DOWN": 11,
109
+ "ATTACHMENT": 14,
110
+ "DATE": 15,
111
+ "DATE_TIME": 16,
112
+ "AREA_PROVINCE": 19,
113
+ "RELATION": 21,
114
+ "SPLIT_LINE": 22,
115
+ "AREA_CITY": 23,
116
+ "AREA_COUNTY": 24,
117
+ "MONEY_CN": 25,
118
+ "USER_PICKER": 26,
119
+ "DEPARTMENT": 27,
120
+ "SCORE": 28,
121
+ "RELATE_SHEET": 29,
122
+ "SHEET_FIELD": 30,
123
+ "FORMULA_NUMBER": 31,
124
+ "CONCATENATE": 32,
125
+ "AUTO_ID": 33,
126
+ "SUB_LIST": 34,
127
+ "CASCADER": 35,
128
+ "SWITCH": 36,
129
+ "SUBTOTAL": 37,
130
+ "FORMULA_DATE": 38,
131
+ "LOCATION": 40,
132
+ "RICH_TEXT": 41,
133
+ "SIGNATURE": 42,
134
+ "OCR": 43,
135
+ "EMBED": 45,
136
+ "TIME": 46,
137
+ "BAR_CODE": 47,
138
+ "ORG_ROLE": 48,
139
+ "SEARCH_BTN": 49,
140
+ "SEARCH": 50,
141
+ "RELATION_SEARCH": 51,
142
+ "SECTION": 52,
143
+ "FORMULA_FUNC": 53,
144
+ }
145
+
146
+
147
+ # ── Worksheet CRUD ───────────────────────────────────────────────────────
148
+
149
+
150
+ def create_worksheet(
151
+ session: Session,
152
+ app_id: str,
153
+ name: str,
154
+ project_id: str = "",
155
+ section_id: str = "",
156
+ ) -> dict[str, Any]:
157
+ """Create a new worksheet."""
158
+ data: dict[str, Any] = {"appId": app_id, "name": name}
159
+ if project_id:
160
+ data["projectId"] = project_id
161
+ if section_id:
162
+ data["appSectionId"] = section_id # API requires appSectionId
163
+ return session.api_call("AppManagement", "AddWorkSheet", data)
164
+
165
+
166
+ def copy_worksheet(
167
+ session: Session,
168
+ worksheet_id: str,
169
+ name: str,
170
+ app_id: str = "",
171
+ project_id: str = "",
172
+ copy_rows: bool = False,
173
+ ) -> dict[str, Any]:
174
+ """Copy a worksheet."""
175
+ data: dict[str, Any] = {"worksheetId": worksheet_id, "name": name}
176
+ if app_id:
177
+ data["appId"] = app_id
178
+ if project_id:
179
+ data["projectId"] = project_id
180
+ data["isCopyRows"] = copy_rows
181
+ return session.api_call("Worksheet", "CopyWorksheet", data)
182
+
183
+
184
+ def update_worksheet(
185
+ session: Session,
186
+ worksheet_id: str,
187
+ alias: str = "",
188
+ desc: str = "",
189
+ app_id: str = "",
190
+ ) -> dict[str, Any]:
191
+ """Update worksheet alias and/or description."""
192
+ result = {}
193
+ if alias:
194
+ result = session.api_call(
195
+ "Worksheet", "UpdateWorksheetAlias",
196
+ {"worksheetId": worksheet_id, "alias": alias, "appId": app_id},
197
+ )
198
+ if desc:
199
+ result = session.api_call(
200
+ "Worksheet", "UpdateWorksheetDec",
201
+ {"worksheetId": worksheet_id, "dec": desc},
202
+ )
203
+ return result
204
+
205
+
206
+ # ── Field/Control management ─────────────────────────────────────────────
207
+
208
+
209
+ def save_controls(
210
+ session: Session,
211
+ worksheet_id: str,
212
+ controls: list[dict[str, Any]],
213
+ ) -> dict[str, Any]:
214
+ """Save the complete controls list for a worksheet (add/update/delete via full replacement).
215
+
216
+ Workflow: call get_worksheet_controls() first to get the current list,
217
+ modify it as needed, then pass the full modified list here.
218
+
219
+ Args:
220
+ session: Active session
221
+ worksheet_id: Worksheet ID (used as sourceId)
222
+ controls: Complete list of control definitions (old + new + modified)
223
+
224
+ Returns:
225
+ API response dict
226
+ """
227
+ return session.api_call(
228
+ "Worksheet", "SaveWorksheetControls",
229
+ {"version": 3, "sourceId": worksheet_id, "controls": controls},
230
+ )
231
+
232
+
233
+ def add_controls(
234
+ session: Session,
235
+ worksheet_id: str,
236
+ controls: list[dict[str, Any]],
237
+ ) -> dict[str, Any]:
238
+ """Add new fields/controls to a worksheet (legacy endpoint)."""
239
+ return session.api_call(
240
+ "Worksheet", "AddWorksheetControls",
241
+ {"worksheetId": worksheet_id, "controls": controls},
242
+ )
243
+
244
+
245
+ # ── View management ──────────────────────────────────────────────────────
246
+
247
+
248
+ def save_view(
249
+ session: Session,
250
+ worksheet_id: str,
251
+ name: str,
252
+ view_type: int = 0,
253
+ view_id: str = "",
254
+ config: Optional[dict] = None,
255
+ app_id: str = "",
256
+ ) -> dict[str, Any]:
257
+ """Create or update a worksheet view."""
258
+ data: dict[str, Any] = {
259
+ "worksheetId": worksheet_id,
260
+ "name": name,
261
+ "viewType": view_type,
262
+ }
263
+ if view_id:
264
+ data["viewId"] = view_id
265
+ if config:
266
+ data.update(config)
267
+ if app_id:
268
+ data["appId"] = app_id
269
+ return session.api_call("Worksheet", "SaveWorksheetView", data)
270
+
271
+
272
+ def delete_view(
273
+ session: Session, worksheet_id: str, view_id: str, app_id: str = "",
274
+ ) -> dict[str, Any]:
275
+ """Delete a worksheet view."""
276
+ data: dict[str, Any] = {"worksheetId": worksheet_id, "viewId": view_id}
277
+ if app_id:
278
+ data["appId"] = app_id
279
+ return session.api_call("Worksheet", "DeleteWorksheetView", data)
280
+
281
+
282
+ def copy_view(
283
+ session: Session, worksheet_id: str, view_id: str, name: str,
284
+ ) -> dict[str, Any]:
285
+ """Copy a worksheet view."""
286
+ return session.api_call(
287
+ "Worksheet", "CopyWorksheetView",
288
+ {"worksheetId": worksheet_id, "viewId": view_id, "viewName": name},
289
+ )
290
+
291
+
292
+ def sort_views(
293
+ session: Session, worksheet_id: str, view_ids: list[str],
294
+ ) -> dict[str, Any]:
295
+ """Reorder worksheet views."""
296
+ return session.api_call(
297
+ "Worksheet", "SortWorksheetViews",
298
+ {"worksheetId": worksheet_id, "viewIds": view_ids},
299
+ )
300
+
301
+
302
+ # ── Business Rules ───────────────────────────────────────────────────────
303
+
304
+
305
+ def get_control_rules(
306
+ session: Session, worksheet_id: str, control_id: str = "",
307
+ ) -> dict[str, Any]:
308
+ """Get business rules for worksheet controls."""
309
+ data: dict[str, Any] = {"worksheetId": worksheet_id}
310
+ if control_id:
311
+ data["controlId"] = control_id
312
+ return session.api_call("Worksheet", "GetControlRules", data)
313
+
314
+
315
+ def save_control_rule(
316
+ session: Session,
317
+ worksheet_id: str,
318
+ rules: list[dict[str, Any]],
319
+ control_id: str = "",
320
+ ) -> dict[str, Any]:
321
+ """Save business rules for worksheet controls."""
322
+ data: dict[str, Any] = {"worksheetId": worksheet_id, "rules": rules}
323
+ if control_id:
324
+ data["controlId"] = control_id
325
+ return session.api_call("Worksheet", "SaveControlRule", data)
326
+
327
+
328
+ # ── Custom Buttons ───────────────────────────────────────────────────────
329
+
330
+
331
+ def get_buttons(
332
+ session: Session, worksheet_id: str,
333
+ ) -> dict[str, Any]:
334
+ """Get custom action buttons for a worksheet."""
335
+ return session.api_call(
336
+ "Worksheet", "GetWorksheetBtns", {"worksheetId": worksheet_id},
337
+ )
338
+
339
+
340
+ def save_button(
341
+ session: Session,
342
+ worksheet_id: str,
343
+ btn_data: dict[str, Any],
344
+ btn_id: str = "",
345
+ ) -> dict[str, Any]:
346
+ """Create or update a custom action button.
347
+
348
+ On creation, the API auto-creates a linked workflow.
349
+ Response 'data' field contains the triggerId (button's workflow trigger ID).
350
+ """
351
+ data: dict[str, Any] = {"worksheetId": worksheet_id}
352
+ if btn_id:
353
+ data["btnId"] = btn_id
354
+ data.update(btn_data)
355
+ return session.api_call("Worksheet", "SaveWorksheetBtn", data)
356
+
357
+
358
+ def delete_button(
359
+ session: Session, worksheet_id: str, btn_id: str,
360
+ ) -> dict[str, Any]:
361
+ """Delete a custom action button."""
362
+ return session.api_call(
363
+ "Worksheet", "SaveWorksheetBtn",
364
+ {"worksheetId": worksheet_id, "btnId": btn_id, "isDelete": True},
365
+ )
366
+
367
+
368
+ # ── Feature Switches ────────────────────────────────────────────────────
369
+
370
+
371
+ def get_switches(
372
+ session: Session, worksheet_id: str,
373
+ ) -> dict[str, Any]:
374
+ """Get feature switches for a worksheet."""
375
+ return session.api_call(
376
+ "Worksheet", "GetSwitch", {"worksheetId": worksheet_id},
377
+ )
378
+
379
+
380
+ def edit_switch(
381
+ session: Session, worksheet_id: str, switch_id: int, value: bool,
382
+ ) -> dict[str, Any]:
383
+ """Edit a single feature switch."""
384
+ return session.api_call(
385
+ "Worksheet", "EditSwitch",
386
+ {"worksheetId": worksheet_id, "state": switch_id, "value": value},
387
+ )
388
+
389
+
390
+ # ── Logs ─────────────────────────────────────────────────────────────────
391
+
392
+
393
+ def get_worksheet_logs(
394
+ session: Session,
395
+ worksheet_id: str,
396
+ page_index: int = 1,
397
+ page_size: int = 50,
398
+ ) -> dict[str, Any]:
399
+ """Get worksheet operation logs."""
400
+ return session.api_call(
401
+ "Worksheet", "GetWorksheetOperationLogs",
402
+ {"worksheetId": worksheet_id, "pageIndex": page_index, "pageSize": page_size},
403
+ )
hap_cli/hap_cli.py ADDED
@@ -0,0 +1,105 @@
1
+ """Main CLI entry point for hap-cli harness.
2
+
3
+ Usage:
4
+ hap [--json] <command> [args...]
5
+ hap repl
6
+ """
7
+
8
+ import cmd
9
+ import shlex
10
+
11
+ import click
12
+
13
+ from hap_cli.context import Context, pass_context
14
+
15
+ # Import command groups
16
+ from hap_cli.commands.config_cmd import config
17
+ from hap_cli.commands.app_cmd import app
18
+ from hap_cli.commands.worksheet_cmd import worksheet
19
+ from hap_cli.commands.record_cmd import record
20
+ from hap_cli.commands.workflow_cmd import workflow
21
+ from hap_cli.commands.node_cmd import node
22
+ from hap_cli.commands.instance_cmd import instance
23
+ from hap_cli.commands.role_cmd import role
24
+ from hap_cli.commands.optionset_cmd import optionset
25
+ from hap_cli.commands.page_cmd import page
26
+ from hap_cli.commands.contact_cmd import contact
27
+ from hap_cli.commands.department_cmd import department
28
+ from hap_cli.commands.post_cmd import post
29
+ from hap_cli.commands.calendar_cmd import calendar
30
+ from hap_cli.commands.chat_cmd import chat
31
+ from hap_cli.commands.group_cmd import group
32
+ from hap_cli.commands.ai_cmd import ai
33
+ from hap_cli.commands.plugin_cmd import plugin
34
+
35
+
36
+ @click.group()
37
+ @click.option("--json", "json_mode", is_flag=True, help="Output in JSON format")
38
+ @click.pass_context
39
+ def cli(ctx, json_mode):
40
+ """CLI harness for MingDAO HAP (hap) - Enterprise no-code platform."""
41
+ ctx.ensure_object(Context)
42
+ ctx.obj.json_mode = json_mode
43
+
44
+
45
+ # Register command groups
46
+ cli.add_command(config)
47
+ cli.add_command(app)
48
+ cli.add_command(worksheet)
49
+ cli.add_command(record)
50
+ cli.add_command(workflow)
51
+ cli.add_command(node)
52
+ cli.add_command(instance)
53
+ cli.add_command(role)
54
+ cli.add_command(optionset)
55
+ cli.add_command(page)
56
+ cli.add_command(contact)
57
+ cli.add_command(department)
58
+ cli.add_command(post)
59
+ cli.add_command(calendar)
60
+ cli.add_command(chat)
61
+ cli.add_command(group)
62
+ cli.add_command(ai)
63
+ cli.add_command(plugin)
64
+
65
+
66
+ # ── REPL ─────────────────────────────────────────────────────────────────
67
+
68
+
69
+ class HapREPL(cmd.Cmd):
70
+ """Interactive REPL for hap-cli."""
71
+
72
+ intro = "hap-cli REPL. Type 'help' for commands, 'quit' to exit."
73
+ prompt = "hap> "
74
+
75
+ def default(self, line):
76
+ """Execute CLI commands."""
77
+ try:
78
+ args = shlex.split(line)
79
+ cli.main(args, standalone_mode=False)
80
+ except SystemExit:
81
+ pass
82
+ except Exception as e:
83
+ click.echo(f"Error: {e}", err=True)
84
+
85
+ def do_quit(self, _arg):
86
+ """Exit the REPL."""
87
+ return True
88
+
89
+ do_exit = do_quit
90
+ do_EOF = do_quit
91
+
92
+
93
+ @cli.command()
94
+ def repl():
95
+ """Start interactive REPL mode."""
96
+ HapREPL().cmdloop()
97
+
98
+
99
+ def main():
100
+ """Entry point for the CLI."""
101
+ cli()
102
+
103
+
104
+ if __name__ == "__main__":
105
+ main()