dailybot-cli 3.1.2__tar.gz → 3.2.0__tar.gz
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.
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/PKG-INFO +32 -2
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/README.md +31 -1
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/api_client.py +26 -0
- dailybot_cli-3.2.0/dailybot_cli/commands/conversation.py +187 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/form.py +17 -2
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/user_scoped_actions.py +2 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/display.py +21 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/main.py +2 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/PKG-INFO +32 -2
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/SOURCES.txt +1 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/pyproject.toml +1 -1
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/LICENSE +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/__init__.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/__init__.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/agent.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/ask.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/auth.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/authoring_helpers.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/channels.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/chat.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/checkin.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/config.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/hook.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/identity.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/interactive.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/interactive_chat.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/kudos.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/public_api_helpers.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/query_options.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/status.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/team.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/uninstall.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/update.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/upgrade.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/user.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/version.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/commands/workflow.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/config.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/install_method.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/ledger.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/__init__.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/app.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/commands.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/conversation.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli/tui/intents.py +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/dependency_links.txt +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/entry_points.txt +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/requires.txt +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/dailybot_cli.egg-info/top_level.txt +0 -0
- {dailybot_cli-3.1.2 → dailybot_cli-3.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dailybot-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more.
|
|
5
5
|
Author-email: Dailybot <support@dailybot.com>
|
|
6
6
|
License: MIT
|
|
@@ -877,7 +877,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
|
|
|
877
877
|
|
|
878
878
|
| Command | Description |
|
|
879
879
|
|---------|-------------|
|
|
880
|
-
| `dailybot form list` | List
|
|
880
|
+
| `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
|
|
881
881
|
| `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
|
|
882
882
|
| `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
|
|
883
883
|
| `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
|
|
@@ -923,6 +923,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
|
|
|
923
923
|
|---------|-------------|
|
|
924
924
|
| `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
|
|
925
925
|
| `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
|
|
926
|
+
| `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
|
|
926
927
|
|
|
927
928
|
```bash
|
|
928
929
|
# To a channel
|
|
@@ -984,6 +985,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
|
|
|
984
985
|
outside your role's reach returns a clear error, as does the per-token CLI rate
|
|
985
986
|
limit.
|
|
986
987
|
|
|
988
|
+
### Conversation commands (Slack group DMs)
|
|
989
|
+
|
|
990
|
+
| Command | Description |
|
|
991
|
+
|---------|-------------|
|
|
992
|
+
| `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
|
|
993
|
+
|
|
994
|
+
```bash
|
|
995
|
+
# Open a group DM with two teammates (by UUID) — prints the channel id
|
|
996
|
+
dailybot conversation open -u <uuid1> -u <uuid2>
|
|
997
|
+
|
|
998
|
+
# Name or email work too (resolved via the org directory)
|
|
999
|
+
dailybot conversation open -u "Mauricio Gomez"
|
|
1000
|
+
dailybot conversation open --emails ana@co.com,luis@co.com
|
|
1001
|
+
|
|
1002
|
+
# Open the group and immediately post a report in one call
|
|
1003
|
+
dailybot conversation open -u "Mauricio Gomez" \
|
|
1004
|
+
-m "Report on the latest analysis — see thread for details."
|
|
1005
|
+
|
|
1006
|
+
# Headless: capture the channel id for scripting
|
|
1007
|
+
CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
**Slack only, org-admin only.** Opening is **idempotent** — the same set of
|
|
1011
|
+
participants always returns the same channel, so "open it, or reuse the existing
|
|
1012
|
+
one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
|
|
1013
|
+
get a clear "Slack only" message; non-admins get a permissions error. The
|
|
1014
|
+
optional `-m/--message` chains a `group_chat` send to the returned channel (the
|
|
1015
|
+
same delivery path as `chat send`).
|
|
1016
|
+
|
|
987
1017
|
### Agent commands
|
|
988
1018
|
|
|
989
1019
|
| Command | Description |
|
|
@@ -838,7 +838,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
|
|
|
838
838
|
|
|
839
839
|
| Command | Description |
|
|
840
840
|
|---------|-------------|
|
|
841
|
-
| `dailybot form list` | List
|
|
841
|
+
| `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
|
|
842
842
|
| `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
|
|
843
843
|
| `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
|
|
844
844
|
| `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
|
|
@@ -884,6 +884,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
|
|
|
884
884
|
|---------|-------------|
|
|
885
885
|
| `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
|
|
886
886
|
| `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
|
|
887
|
+
| `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
|
|
887
888
|
|
|
888
889
|
```bash
|
|
889
890
|
# To a channel
|
|
@@ -945,6 +946,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
|
|
|
945
946
|
outside your role's reach returns a clear error, as does the per-token CLI rate
|
|
946
947
|
limit.
|
|
947
948
|
|
|
949
|
+
### Conversation commands (Slack group DMs)
|
|
950
|
+
|
|
951
|
+
| Command | Description |
|
|
952
|
+
|---------|-------------|
|
|
953
|
+
| `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
|
|
954
|
+
|
|
955
|
+
```bash
|
|
956
|
+
# Open a group DM with two teammates (by UUID) — prints the channel id
|
|
957
|
+
dailybot conversation open -u <uuid1> -u <uuid2>
|
|
958
|
+
|
|
959
|
+
# Name or email work too (resolved via the org directory)
|
|
960
|
+
dailybot conversation open -u "Mauricio Gomez"
|
|
961
|
+
dailybot conversation open --emails ana@co.com,luis@co.com
|
|
962
|
+
|
|
963
|
+
# Open the group and immediately post a report in one call
|
|
964
|
+
dailybot conversation open -u "Mauricio Gomez" \
|
|
965
|
+
-m "Report on the latest analysis — see thread for details."
|
|
966
|
+
|
|
967
|
+
# Headless: capture the channel id for scripting
|
|
968
|
+
CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
**Slack only, org-admin only.** Opening is **idempotent** — the same set of
|
|
972
|
+
participants always returns the same channel, so "open it, or reuse the existing
|
|
973
|
+
one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
|
|
974
|
+
get a clear "Slack only" message; non-admins get a permissions error. The
|
|
975
|
+
optional `-m/--message` chains a `group_chat` send to the returned channel (the
|
|
976
|
+
same delivery path as `chat send`).
|
|
977
|
+
|
|
948
978
|
### Agent commands
|
|
949
979
|
|
|
950
980
|
| Command | Description |
|
|
@@ -795,6 +795,7 @@ class DailyBotClient:
|
|
|
795
795
|
*,
|
|
796
796
|
include_questions: bool = False,
|
|
797
797
|
include_archived: bool = False,
|
|
798
|
+
owner: str | None = None,
|
|
798
799
|
search: str | None = None,
|
|
799
800
|
start_date: str | None = None,
|
|
800
801
|
end_date: str | None = None,
|
|
@@ -806,6 +807,9 @@ class DailyBotClient:
|
|
|
806
807
|
) -> list[dict[str, Any]]:
|
|
807
808
|
"""GET /v1/forms/ — optionally expand questions, search, and page.
|
|
808
809
|
|
|
810
|
+
The default response is org-scoped (every form the caller can see on the
|
|
811
|
+
webapp list view). Pass ``owner="me"`` to narrow to the caller's own forms.
|
|
812
|
+
|
|
809
813
|
When ``meta`` is given it is populated with ``count`` / ``next`` for a
|
|
810
814
|
pagination footer.
|
|
811
815
|
"""
|
|
@@ -814,6 +818,8 @@ class DailyBotClient:
|
|
|
814
818
|
params["include"] = "questions"
|
|
815
819
|
if include_archived:
|
|
816
820
|
params["include_archived"] = "true"
|
|
821
|
+
if owner:
|
|
822
|
+
params["owner"] = owner
|
|
817
823
|
_merge_list_query(params, search=search, start_date=start_date, end_date=end_date)
|
|
818
824
|
result: PaginatedResult = self._paginated_get(
|
|
819
825
|
f"{self.api_url}/v1/forms/",
|
|
@@ -1493,6 +1499,26 @@ class DailyBotClient:
|
|
|
1493
1499
|
)
|
|
1494
1500
|
return self._handle_response(response)
|
|
1495
1501
|
|
|
1502
|
+
def open_conversation(self, users_uuids: list[str]) -> dict[str, Any]:
|
|
1503
|
+
"""POST /v1/open-conversation/ — open (or fetch) a Slack group DM (MPIM).
|
|
1504
|
+
|
|
1505
|
+
Opens a private Slack group conversation that includes the given org
|
|
1506
|
+
users plus the Dailybot bot (Slack adds the bot because the call uses the
|
|
1507
|
+
org's bot token). The call is idempotent: the same set of users returns
|
|
1508
|
+
the same channel if it already exists.
|
|
1509
|
+
|
|
1510
|
+
Slack-only and org-admin-only, both server-enforced. Authenticated via
|
|
1511
|
+
the shared agent header logic (``X-API-KEY`` preferred, else the login
|
|
1512
|
+
Bearer token). Returns ``{"channel": "<slack-conversation-id>"}``.
|
|
1513
|
+
"""
|
|
1514
|
+
response: httpx.Response = httpx.post(
|
|
1515
|
+
f"{self.api_url}/v1/open-conversation/",
|
|
1516
|
+
json={"users_uuids": users_uuids},
|
|
1517
|
+
headers=self._agent_headers(),
|
|
1518
|
+
timeout=self.timeout,
|
|
1519
|
+
)
|
|
1520
|
+
return self._handle_response(response)
|
|
1521
|
+
|
|
1496
1522
|
# --- Agent message endpoints ---
|
|
1497
1523
|
|
|
1498
1524
|
def send_agent_message(
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""``conversation`` command group — open a Slack group DM (MPIM) that includes
|
|
2
|
+
the Dailybot bot, and optionally post a message to it.
|
|
3
|
+
|
|
4
|
+
Wraps ``POST /v1/open-conversation/`` (Slack-only, org-admin-only, idempotent)
|
|
5
|
+
and chains the existing ``POST /v1/send-message/`` path with
|
|
6
|
+
``channel_type=group_chat`` when ``--message`` is given. Authenticated with a
|
|
7
|
+
login Bearer token or an org API key. Participants can be named by UUID, email,
|
|
8
|
+
or name — non-UUID identifiers are resolved client-side via ``GET /v1/users/``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Any, NoReturn
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from dailybot_cli.api_client import APIError, DailyBotClient
|
|
16
|
+
from dailybot_cli.commands.public_api_helpers import (
|
|
17
|
+
UUID_PATTERN,
|
|
18
|
+
emit_json,
|
|
19
|
+
require_auth,
|
|
20
|
+
resolve_user_by_name_or_uuid,
|
|
21
|
+
)
|
|
22
|
+
from dailybot_cli.display import (
|
|
23
|
+
console,
|
|
24
|
+
print_conversation_result,
|
|
25
|
+
print_error,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
GROUP_CHAT_TYPE: str = "group_chat"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _split_identifiers(values: tuple[str, ...]) -> list[str]:
|
|
32
|
+
"""Flatten repeatable and comma-separated option values into a clean list."""
|
|
33
|
+
out: list[str] = []
|
|
34
|
+
for value in values:
|
|
35
|
+
out.extend(part.strip() for part in value.split(",") if part.strip())
|
|
36
|
+
return out
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _resolve_participants(client: DailyBotClient, identifiers: list[str]) -> list[tuple[str, str]]:
|
|
40
|
+
"""Resolve each identifier (UUID, email, or name) to a (uuid, name) pair.
|
|
41
|
+
|
|
42
|
+
The user directory is fetched only when at least one identifier is not a bare
|
|
43
|
+
UUID, so an all-UUID call avoids the extra round-trip. Order is preserved and
|
|
44
|
+
duplicate UUIDs are dropped (keeping the first occurrence).
|
|
45
|
+
"""
|
|
46
|
+
needs_directory: bool = any(not UUID_PATTERN.match(ident) for ident in identifiers)
|
|
47
|
+
directory: list[dict[str, Any]] = (
|
|
48
|
+
client.list_users(include_email=True) if needs_directory else []
|
|
49
|
+
)
|
|
50
|
+
resolved: list[tuple[str, str]] = []
|
|
51
|
+
seen: set[str] = set()
|
|
52
|
+
for ident in identifiers:
|
|
53
|
+
uuid, name = resolve_user_by_name_or_uuid(directory, ident)
|
|
54
|
+
if uuid in seen:
|
|
55
|
+
continue
|
|
56
|
+
seen.add(uuid)
|
|
57
|
+
resolved.append((uuid, name))
|
|
58
|
+
return resolved
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _exit_for_conversation_error(exc: APIError, json_mode: bool) -> NoReturn:
|
|
62
|
+
"""Translate an open-conversation / send failure to a friendly message + exit."""
|
|
63
|
+
code: str | None = getattr(exc, "code", None)
|
|
64
|
+
if code == "open_conversation_not_supported" or exc.status_code == 406:
|
|
65
|
+
message: str = "Group conversations are only supported for Slack workspaces."
|
|
66
|
+
elif code == "one_or_more_users_not_found":
|
|
67
|
+
message = "One or more users were not found or aren't active in your organization."
|
|
68
|
+
elif code == "no_valid_users":
|
|
69
|
+
message = "The participant list contains invalid user UUIDs."
|
|
70
|
+
elif code == "conversation_can_not_be_opened":
|
|
71
|
+
message = "Slack rejected the request. A participant may be deactivated on the Slack side."
|
|
72
|
+
elif exc.status_code == 403:
|
|
73
|
+
message = "This command requires organization admin privileges."
|
|
74
|
+
elif exc.status_code == 401:
|
|
75
|
+
message = "Session expired. Run: dailybot login"
|
|
76
|
+
elif exc.status_code == 429:
|
|
77
|
+
message = "Rate limit exceeded. Wait a moment and try again."
|
|
78
|
+
else:
|
|
79
|
+
message = exc.detail
|
|
80
|
+
if json_mode:
|
|
81
|
+
payload: dict[str, Any] = {"error": message, "status": exc.status_code}
|
|
82
|
+
if code:
|
|
83
|
+
payload["code"] = code
|
|
84
|
+
emit_json(payload)
|
|
85
|
+
else:
|
|
86
|
+
print_error(message)
|
|
87
|
+
raise SystemExit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@click.group(name="conversation")
|
|
91
|
+
def conversation() -> None:
|
|
92
|
+
"""Open Slack group DMs (with the Dailybot bot) and post to them.
|
|
93
|
+
|
|
94
|
+
\b
|
|
95
|
+
Slack only, organization admin only. Opens a private group DM (MPIM) that
|
|
96
|
+
includes the Dailybot bot plus the users you name, then optionally posts a
|
|
97
|
+
message. Opening is idempotent — the same set of users returns the same
|
|
98
|
+
channel.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@conversation.command("open")
|
|
103
|
+
@click.option(
|
|
104
|
+
"--user",
|
|
105
|
+
"--users",
|
|
106
|
+
"-u",
|
|
107
|
+
"users_raw",
|
|
108
|
+
multiple=True,
|
|
109
|
+
help="Participant by UUID, email, or name (repeatable or comma-separated).",
|
|
110
|
+
)
|
|
111
|
+
@click.option(
|
|
112
|
+
"--email",
|
|
113
|
+
"--emails",
|
|
114
|
+
"-e",
|
|
115
|
+
"emails_raw",
|
|
116
|
+
multiple=True,
|
|
117
|
+
help="Participant by email (repeatable or comma-separated); resolved to a UUID.",
|
|
118
|
+
)
|
|
119
|
+
@click.option(
|
|
120
|
+
"--message",
|
|
121
|
+
"-m",
|
|
122
|
+
default=None,
|
|
123
|
+
help="Optional message to post to the group right after opening it.",
|
|
124
|
+
)
|
|
125
|
+
@click.option("--json", "json_mode", is_flag=True, help="Emit machine-readable JSON to stdout.")
|
|
126
|
+
def conversation_open(
|
|
127
|
+
users_raw: tuple[str, ...],
|
|
128
|
+
emails_raw: tuple[str, ...],
|
|
129
|
+
message: str | None,
|
|
130
|
+
json_mode: bool,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Open (or fetch) a Slack group DM and optionally post a message.
|
|
133
|
+
|
|
134
|
+
\b
|
|
135
|
+
Slack only, org admin only. Idempotent — the same participants return the
|
|
136
|
+
same channel. Participants accept UUID, email, or name; --emails is a
|
|
137
|
+
convenience alias that only takes emails.
|
|
138
|
+
|
|
139
|
+
\b
|
|
140
|
+
Examples:
|
|
141
|
+
dailybot conversation open -u <uuid1> -u <uuid2>
|
|
142
|
+
dailybot conversation open -u Mauricio -m "Report on the latest analysis"
|
|
143
|
+
dailybot conversation open --emails ana@co.com,luis@co.com --json
|
|
144
|
+
"""
|
|
145
|
+
identifiers: list[str] = _split_identifiers(users_raw) + _split_identifiers(emails_raw)
|
|
146
|
+
if not identifiers:
|
|
147
|
+
print_error("Provide at least one participant with --user (UUID, email, or name).")
|
|
148
|
+
raise SystemExit(1)
|
|
149
|
+
|
|
150
|
+
client: DailyBotClient = require_auth()
|
|
151
|
+
try:
|
|
152
|
+
participants: list[tuple[str, str]] = _resolve_participants(client, identifiers)
|
|
153
|
+
except ValueError as exc:
|
|
154
|
+
print_error(str(exc))
|
|
155
|
+
raise SystemExit(1)
|
|
156
|
+
|
|
157
|
+
uuids: list[str] = [uuid for uuid, _name in participants]
|
|
158
|
+
try:
|
|
159
|
+
with console.status("Opening group conversation..."):
|
|
160
|
+
result: dict[str, Any] = client.open_conversation(uuids)
|
|
161
|
+
except APIError as exc:
|
|
162
|
+
_exit_for_conversation_error(exc, json_mode)
|
|
163
|
+
|
|
164
|
+
channel: str = str(result.get("channel", ""))
|
|
165
|
+
message_sent: bool = False
|
|
166
|
+
if message:
|
|
167
|
+
payload: dict[str, Any] = {
|
|
168
|
+
"message": message,
|
|
169
|
+
"target_channels": [{"id": channel, "channel_type": GROUP_CHAT_TYPE}],
|
|
170
|
+
}
|
|
171
|
+
try:
|
|
172
|
+
with console.status("Sending message..."):
|
|
173
|
+
client.send_chat_message(payload)
|
|
174
|
+
except APIError as exc:
|
|
175
|
+
_exit_for_conversation_error(exc, json_mode)
|
|
176
|
+
message_sent = True
|
|
177
|
+
|
|
178
|
+
if json_mode:
|
|
179
|
+
emit_json(
|
|
180
|
+
{
|
|
181
|
+
"channel": channel,
|
|
182
|
+
"participants": [{"uuid": uuid, "name": name} for uuid, name in participants],
|
|
183
|
+
"message_sent": message_sent,
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
return
|
|
187
|
+
print_conversation_result(channel, participants, message_sent=message_sent)
|
|
@@ -188,10 +188,16 @@ def form() -> None:
|
|
|
188
188
|
is_flag=True,
|
|
189
189
|
help="Include archived forms (hidden by default).",
|
|
190
190
|
)
|
|
191
|
+
@click.option(
|
|
192
|
+
"--mine",
|
|
193
|
+
is_flag=True,
|
|
194
|
+
help="Only forms you own (default lists every form you can see in the org).",
|
|
195
|
+
)
|
|
191
196
|
@query_options
|
|
192
197
|
@click.option("--json", "json_mode", is_flag=True, help="Emit machine-readable JSON to stdout.")
|
|
193
198
|
def form_list(
|
|
194
199
|
include_archived: bool,
|
|
200
|
+
mine: bool,
|
|
195
201
|
json_mode: bool,
|
|
196
202
|
page: int | None,
|
|
197
203
|
page_size: int | None,
|
|
@@ -207,12 +213,15 @@ def form_list(
|
|
|
207
213
|
"""List forms visible to you.
|
|
208
214
|
|
|
209
215
|
\b
|
|
210
|
-
Acts as you. You can only see and act on what you could in the webapp
|
|
216
|
+
Acts as you. You can only see and act on what you could in the webapp:
|
|
217
|
+
by default this is every form in your org you have list-view access to.
|
|
218
|
+
Pass --mine to narrow to only the forms you own.
|
|
211
219
|
Archived forms are hidden unless you pass --include-archived.
|
|
212
220
|
|
|
213
221
|
\b
|
|
214
222
|
Examples:
|
|
215
223
|
dailybot form list
|
|
224
|
+
dailybot form list --mine
|
|
216
225
|
dailybot form list --search retro --since 2026-07-01
|
|
217
226
|
dailybot form list --all
|
|
218
227
|
dailybot form list --json
|
|
@@ -235,7 +244,13 @@ def form_list(
|
|
|
235
244
|
print_error(str(exc))
|
|
236
245
|
raise SystemExit(1)
|
|
237
246
|
client = require_auth()
|
|
238
|
-
execute_form_list(
|
|
247
|
+
execute_form_list(
|
|
248
|
+
client,
|
|
249
|
+
json_mode=json_mode,
|
|
250
|
+
include_archived=include_archived,
|
|
251
|
+
owner="me" if mine else None,
|
|
252
|
+
spec=spec,
|
|
253
|
+
)
|
|
239
254
|
|
|
240
255
|
|
|
241
256
|
@form.command("get")
|
|
@@ -500,6 +500,7 @@ def execute_form_list(
|
|
|
500
500
|
*,
|
|
501
501
|
json_mode: bool = False,
|
|
502
502
|
include_archived: bool = False,
|
|
503
|
+
owner: str | None = None,
|
|
503
504
|
spec: QuerySpec | None = None,
|
|
504
505
|
) -> list[dict[str, Any]] | None:
|
|
505
506
|
"""Fetch and display forms visible to the user (with question counts)."""
|
|
@@ -511,6 +512,7 @@ def execute_form_list(
|
|
|
511
512
|
forms: list[dict[str, Any]] = client.list_forms(
|
|
512
513
|
include_questions=True,
|
|
513
514
|
include_archived=include_archived,
|
|
515
|
+
owner=owner,
|
|
514
516
|
search=query.params.get("search"),
|
|
515
517
|
start_date=query.params.get("start_date"),
|
|
516
518
|
end_date=query.params.get("end_date"),
|
|
@@ -406,6 +406,27 @@ def print_chat_message_result(data: dict[str, Any], *, updated: bool = False) ->
|
|
|
406
406
|
console.print(Panel(table, title=f"[bold]{title}[/bold]", border_style="green"))
|
|
407
407
|
|
|
408
408
|
|
|
409
|
+
def print_conversation_result(
|
|
410
|
+
channel: str,
|
|
411
|
+
participants: list[tuple[str, str]],
|
|
412
|
+
*,
|
|
413
|
+
message_sent: bool = False,
|
|
414
|
+
) -> None:
|
|
415
|
+
"""Display the opened Slack group conversation and its participants.
|
|
416
|
+
|
|
417
|
+
``participants`` is a list of ``(uuid, display_name)`` pairs. ``message_sent``
|
|
418
|
+
flags whether a message was posted to the group right after opening it.
|
|
419
|
+
"""
|
|
420
|
+
table: Table = Table(show_header=False, box=None, padding=(0, 2))
|
|
421
|
+
table.add_column(style="bold cyan", no_wrap=True)
|
|
422
|
+
table.add_column()
|
|
423
|
+
table.add_row("Channel", channel)
|
|
424
|
+
table.add_row("Participants", ", ".join(name for _uuid, name in participants) or "[dim]—[/dim]")
|
|
425
|
+
if message_sent:
|
|
426
|
+
table.add_row("Message", "Sent to the group")
|
|
427
|
+
console.print(Panel(table, title="[bold]Group Conversation[/bold]", border_style="green"))
|
|
428
|
+
|
|
429
|
+
|
|
409
430
|
def print_agent_report_result(result: dict[str, Any]) -> None:
|
|
410
431
|
"""Display the result of submitting an agent activity report.
|
|
411
432
|
|
|
@@ -12,6 +12,7 @@ from dailybot_cli.commands.channels import channels
|
|
|
12
12
|
from dailybot_cli.commands.chat import chat
|
|
13
13
|
from dailybot_cli.commands.checkin import checkin
|
|
14
14
|
from dailybot_cli.commands.config import config
|
|
15
|
+
from dailybot_cli.commands.conversation import conversation
|
|
15
16
|
from dailybot_cli.commands.form import form
|
|
16
17
|
from dailybot_cli.commands.hook import hook
|
|
17
18
|
from dailybot_cli.commands.identity import me, org
|
|
@@ -86,6 +87,7 @@ cli.add_command(org)
|
|
|
86
87
|
cli.add_command(workflow)
|
|
87
88
|
cli.add_command(agent)
|
|
88
89
|
cli.add_command(chat)
|
|
90
|
+
cli.add_command(conversation)
|
|
89
91
|
cli.add_command(ask)
|
|
90
92
|
cli.add_command(interactive)
|
|
91
93
|
cli.add_command(config)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dailybot-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more.
|
|
5
5
|
Author-email: Dailybot <support@dailybot.com>
|
|
6
6
|
License: MIT
|
|
@@ -877,7 +877,7 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
|
|
|
877
877
|
|
|
878
878
|
| Command | Description |
|
|
879
879
|
|---------|-------------|
|
|
880
|
-
| `dailybot form list` | List
|
|
880
|
+
| `dailybot form list` | List every form in your org you can see (includes question count); `--mine` narrows to forms you own |
|
|
881
881
|
| `dailybot form get <uuid>` | Show the form's full payload (questions + workflow states) |
|
|
882
882
|
| `dailybot form submit <uuid>` | Submit a form (guided prompts or `--content` JSON) |
|
|
883
883
|
| `dailybot form responses <uuid>` | List your own responses on a form (`--state`, `--latest`) |
|
|
@@ -923,6 +923,7 @@ Works headless for agents (`--json` prints the `bot_message_id`).
|
|
|
923
923
|
|---------|-------------|
|
|
924
924
|
| `dailybot chat send` | Send a bot message to users (`-u`), channels (`-c`), and/or teams (`-t`) |
|
|
925
925
|
| `dailybot chat update <bot_message_id>` | Edit a previously sent message (parent **or** a thread reply) |
|
|
926
|
+
| `dailybot conversation open` | Open (or reuse) a Slack group DM with the bot; optionally post a message (`-m`) |
|
|
926
927
|
|
|
927
928
|
```bash
|
|
928
929
|
# To a channel
|
|
@@ -984,6 +985,35 @@ belong to); an org API key (`dailybot config key=...`) is org-wide. A target
|
|
|
984
985
|
outside your role's reach returns a clear error, as does the per-token CLI rate
|
|
985
986
|
limit.
|
|
986
987
|
|
|
988
|
+
### Conversation commands (Slack group DMs)
|
|
989
|
+
|
|
990
|
+
| Command | Description |
|
|
991
|
+
|---------|-------------|
|
|
992
|
+
| `dailybot conversation open` | Open (or fetch) a Slack group DM that includes the Dailybot bot, and optionally post a message to it |
|
|
993
|
+
|
|
994
|
+
```bash
|
|
995
|
+
# Open a group DM with two teammates (by UUID) — prints the channel id
|
|
996
|
+
dailybot conversation open -u <uuid1> -u <uuid2>
|
|
997
|
+
|
|
998
|
+
# Name or email work too (resolved via the org directory)
|
|
999
|
+
dailybot conversation open -u "Mauricio Gomez"
|
|
1000
|
+
dailybot conversation open --emails ana@co.com,luis@co.com
|
|
1001
|
+
|
|
1002
|
+
# Open the group and immediately post a report in one call
|
|
1003
|
+
dailybot conversation open -u "Mauricio Gomez" \
|
|
1004
|
+
-m "Report on the latest analysis — see thread for details."
|
|
1005
|
+
|
|
1006
|
+
# Headless: capture the channel id for scripting
|
|
1007
|
+
CH=$(dailybot conversation open -u <uuid1> -u <uuid2> --json | jq -r .channel)
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
**Slack only, org-admin only.** Opening is **idempotent** — the same set of
|
|
1011
|
+
participants always returns the same channel, so "open it, or reuse the existing
|
|
1012
|
+
one" is a single call. The Dailybot bot is added automatically. Non-Slack orgs
|
|
1013
|
+
get a clear "Slack only" message; non-admins get a permissions error. The
|
|
1014
|
+
optional `-m/--message` chains a `group_chat` send to the returned channel (the
|
|
1015
|
+
same delivery path as `chat send`).
|
|
1016
|
+
|
|
987
1017
|
### Agent commands
|
|
988
1018
|
|
|
989
1019
|
| Command | Description |
|
|
@@ -23,6 +23,7 @@ dailybot_cli/commands/channels.py
|
|
|
23
23
|
dailybot_cli/commands/chat.py
|
|
24
24
|
dailybot_cli/commands/checkin.py
|
|
25
25
|
dailybot_cli/commands/config.py
|
|
26
|
+
dailybot_cli/commands/conversation.py
|
|
26
27
|
dailybot_cli/commands/form.py
|
|
27
28
|
dailybot_cli/commands/hook.py
|
|
28
29
|
dailybot_cli/commands/identity.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dailybot-cli"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.2.0"
|
|
8
8
|
description = "Dailybot CLI - The command-line bridge between humans and agents. Progress reports, observability, and workflow automation for modern teams across Slack, Google Chat, Discord, MS Teams, and more."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|