dailybot-cli 3.19.1__tar.gz → 3.21.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.19.1 → dailybot_cli-3.21.0}/PKG-INFO +24 -19
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/README.md +23 -18
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/api_client.py +148 -1
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_attachments.py +32 -3
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/board.py +4 -37
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/goal.py +3 -12
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/project.py +486 -71
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/public_api_helpers.py +17 -47
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/task.py +2 -24
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/display.py +37 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/PKG-INFO +24 -19
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/pyproject.toml +1 -1
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/LICENSE +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/__init__.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/__init__.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_beta.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_briefing.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_destructive.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_favorites.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_rollups.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/_writes.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/agent.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/ask.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/auth.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/authoring_helpers.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/channels.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/chat.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/checkin.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/config.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/conversation.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/env.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/featured.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/form.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/hook.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/identity.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/interactive.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/interactive_chat.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/kudos.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/label.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/query_options.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/status.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/tasks.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/team.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/uninstall.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/update.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/upgrade.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/user.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/user_scoped_actions.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/version.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/commands/workflow.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/config.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/install_method.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/ledger.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/main.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/tui/__init__.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/tui/app.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/tui/commands.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/tui/conversation.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli/tui/intents.py +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/SOURCES.txt +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/dependency_links.txt +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/entry_points.txt +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/requires.txt +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.0}/dailybot_cli.egg-info/top_level.txt +0 -0
- {dailybot_cli-3.19.1 → dailybot_cli-3.21.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.21.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
|
|
@@ -905,11 +905,13 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
|
|
|
905
905
|
Projects, boards and tasks. Two groups: **`dailybot tasks`** answers questions about the
|
|
906
906
|
workspace, **`dailybot task`** reads or changes one task.
|
|
907
907
|
|
|
908
|
-
**
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
908
|
+
**Your personal API key is you.** A login session (`dailybot login`) and a personal API key
|
|
909
|
+
(one bound to you) can do everything you can do in the web app: create, update, archive and
|
|
910
|
+
restore boards, columns, projects, goals and milestones, manage board and project
|
|
911
|
+
membership, link goals to projects, and work every task. That is every non-guest member, no
|
|
912
|
+
organization-admin prerequisite. Only an agent or organization key, with nobody behind it,
|
|
913
|
+
cannot act as a person: the server refuses it (`actor_required` exit 3, `insufficient_scope`
|
|
914
|
+
exit 4). Guests stay limited by their role.
|
|
913
915
|
**Privacy is membership**, not org role: a `members` project or board is 404 (not visible)
|
|
914
916
|
to anyone without a grant; invite a person or a team to close it.
|
|
915
917
|
A board inside a `members` project follows the project's membership: whoever the project
|
|
@@ -954,9 +956,9 @@ humans who own them.
|
|
|
954
956
|
| `dailybot task comments <uuid>` | List a task's comments |
|
|
955
957
|
| `dailybot task link <a> <b> --type <rel>` | Relate two tasks (`blocks`, `relates_to`, `duplicates`) |
|
|
956
958
|
| `dailybot task labels <uuid> --mode add\|remove\|replace` | Change a task's labels |
|
|
957
|
-
| `dailybot task participants add\|list\|remove <task>` | Who is on a task (`--role participant\|watcher`); `
|
|
959
|
+
| `dailybot task participants add\|list\|remove <task>` | Who is on a task (`--role participant\|watcher`); all need a person: `dailybot login` or a personal API key |
|
|
958
960
|
| `dailybot task watch\|unwatch <task>` | Follow a task privately — **needs a person: `dailybot login` or a personal API key** |
|
|
959
|
-
| `dailybot task mute\|unmute <task>` | Silence a task and stay on it (leaving is `participants remove`) — **needs `dailybot login
|
|
961
|
+
| `dailybot task mute\|unmute <task>` | Silence a task and stay on it (leaving is `participants remove`) — **needs a person: `dailybot login` or a personal API key** |
|
|
960
962
|
| `dailybot task relations <task>` · `task unlink <task> <relation>` | List links (direction included) / remove one; `unlink` confirms and has `--dry-run` |
|
|
961
963
|
| `dailybot task comment-edit\|comment-delete <task> <comment>` | Edit (`-` reads stdin) or delete a comment; delete blanks the text and keeps the entry |
|
|
962
964
|
| `dailybot task children <task>` · `task events <task>` · `task activity <task>` | Sub-tasks, raw event history, and the readable activity feed (`--updated-since`, `--type`) |
|
|
@@ -978,43 +980,46 @@ humans who own them.
|
|
|
978
980
|
| `dailybot board labels <uuid>` | Labels available on the board — **needs a person: `dailybot login` or a personal API key** |
|
|
979
981
|
| `dailybot board views <uuid>` | Your saved views on the board, plus the ETag a save needs (`--etag` prints only that) |
|
|
980
982
|
| `dailybot board state create\|update\|archive\|restore\|reorder` | Manage columns. `archive` previews first and takes `--migrate-to <state>` to move the column's cards |
|
|
981
|
-
| `dailybot board member add <board> <user>` (or `--team <team>`) · `board member remove <board> <user>` | Who can see the board — **needs `dailybot login
|
|
983
|
+
| `dailybot board member add <board> <user>` (or `--team <team>`) · `board member remove <board> <user>` | Who can see the board — **needs a person: `dailybot login` or a personal API key**. Membership is the privacy control (not org role). A team grant follows the team live. Last grant on a private board stays (`last_grant_cannot_be_removed`) |
|
|
982
984
|
| `dailybot board label create <board> -n <name>` | Create an organization label from the board — **needs a person: `dailybot login` or a personal API key** |
|
|
983
985
|
| `dailybot board view save <board> -f views.json --if-match <etag>` | Replace your saved views (the whole list) — **needs a person: `dailybot login` or a personal API key** |
|
|
984
986
|
| `dailybot board snapshot <uuid>` | The whole board in one request; carries the `delta_cursor` that `tasks changes` consumes |
|
|
985
987
|
| `dailybot board update <uuid>` | Name, key (the old key stays reserved), visibility, estimate scale, auto-archive, project |
|
|
986
|
-
| `dailybot board create --name <n> --project <uuid> --key <KEY>` | Create a board in a project; the key prefixes its tasks (`DSN-1`) — **needs `dailybot login
|
|
988
|
+
| `dailybot board create --name <n> --project <uuid> --key <KEY>` | Create a board in a project; the key prefixes its tasks (`DSN-1`) — **needs a person: `dailybot login` or a personal API key** (any non-guest member) |
|
|
987
989
|
| `dailybot board archive <uuid>` | Archive a board. **Cascade-archives its live tasks**, and restoring does not bring them back |
|
|
988
990
|
| `dailybot board restore <uuid>` | Restore a board (cascaded tasks stay archived) |
|
|
989
991
|
| `dailybot project list` | List projects (`--include progress`) |
|
|
990
992
|
| `dailybot project get <uuid>` | Show one project |
|
|
991
993
|
| `dailybot project updates [<project>]` | Batched update digest, or one project's feed |
|
|
992
994
|
| `dailybot project update-post <uuid> <body>` | **Post a project update** — how the team sees what was done (`--health`; sends an idempotency key) |
|
|
995
|
+
| `dailybot project update-get\|update-edit\|update-delete <project> <update>` | Read one update (author, `via <agent>`, health, files); edit its text or health (author only); delete it (author or org admin; `--dry-run`) |
|
|
996
|
+
| `dailybot project update-attach\|update-attachments <project> <update>` · `update-attachment get\|rename\|delete` | Files on an update (≤5 MiB). Attach and rename: author only; delete: author or an organization admin. For an inline image, attach it, then `update-edit` the body with `attachment:<uuid>` |
|
|
993
997
|
| `dailybot project milestones [<uuid>]` | List milestones |
|
|
994
998
|
| `dailybot project milestone-complete <p> <m>` | Complete a milestone. **Its open tasks stay open** |
|
|
995
999
|
| `dailybot project milestone-reopen <p> <m>` | Reopen a milestone |
|
|
996
|
-
| `dailybot project create --name <n>` | Create a project — **needs `dailybot login
|
|
1000
|
+
| `dailybot project create --name <n>` | Create a project — **needs a person: `dailybot login` or a personal API key** |
|
|
997
1001
|
| `dailybot project update <uuid>` | Name, description, lead, health, dates, visibility |
|
|
998
1002
|
| `dailybot project restore <uuid>` | Restore an archived project (its boards and tasks stay archived) |
|
|
999
|
-
| `dailybot project members <uuid>` · `project member add\|remove` | Who can see the project — people or whole teams (`--team`) — `members` works with a personal API key; `member add\|remove` **needs `dailybot login
|
|
1000
|
-
| `dailybot project views <uuid>` · `project view save` | Your saved views; save replaces the list and requires the ETag — **needs `dailybot login
|
|
1003
|
+
| `dailybot project members <uuid>` · `project member add\|remove` | Who can see the project — people or whole teams (`--team`) — `members` works with a personal API key; `member add\|remove` **needs a person: `dailybot login` or a personal API key** |
|
|
1004
|
+
| `dailybot project views <uuid>` · `project view save` | Your saved views; save replaces the list and requires the ETag — **needs a person: `dailybot login` or a personal API key** |
|
|
1001
1005
|
| `dailybot project milestone-create\|milestone-update\|milestone-delete` | Dated milestones; delete retires it (tasks keep pointing at it) |
|
|
1006
|
+
| `dailybot project milestone-attach\|milestone-attachments <project> <milestone>` · `milestone-attachment get\|rename\|delete` | Files on a milestone (≤5 MiB); reference one in the description with `attachment:<uuid>`. `project milestone-restore` brings a retired milestone back |
|
|
1002
1007
|
| `dailybot project archive <uuid>` | Archive a project |
|
|
1003
|
-
| `dailybot project attach <uuid> <file>` · `project attachments` · `project attachment get\|delete` | Files on a project (≤5 MiB, one request). Attaching and deleting need `dailybot login` |
|
|
1008
|
+
| `dailybot project attach <uuid> <file>` · `project attachments` · `project attachment get\|delete` | Files on a project (≤5 MiB, one request). Attaching and deleting need a person (`dailybot login` or a personal API key) |
|
|
1004
1009
|
| `dailybot goal list` | List goals (`--include` is repeatable: `--include progress --include projects`) |
|
|
1005
1010
|
| `dailybot goal get <uuid>` | Show one goal, with its progress and linked projects (always included) |
|
|
1006
|
-
| `dailybot goal create --name <n> --period-start <d> --period-end <d>` | Create a goal (a dated commitment) — **needs `dailybot login
|
|
1011
|
+
| `dailybot goal create --name <n> --period-start <d> --period-end <d>` | Create a goal (a dated commitment) — **needs a person: `dailybot login` or a personal API key** |
|
|
1007
1012
|
| `dailybot goal update <uuid>` | Name, period, owner, team, and the declared `--status` (not_started, on_track, at_risk, off_track, achieved, missed) |
|
|
1008
1013
|
| `dailybot goal restore <uuid>` | Restore an archived goal |
|
|
1009
1014
|
| `dailybot goal link\|unlink <goal> <project>` | Make a project count toward a goal (or stop it) |
|
|
1010
1015
|
| `dailybot goal archive <uuid>` | Archive a goal (its projects are not archived) |
|
|
1011
|
-
| `dailybot goal attach <uuid> <file>` · `goal attachments` · `goal attachment get\|delete` | Files on a goal (≤5 MiB, one request). Attaching and deleting need `dailybot login` |
|
|
1016
|
+
| `dailybot goal attach <uuid> <file>` · `goal attachments` · `goal attachment get\|delete` | Files on a goal (≤5 MiB, one request). Attaching and deleting need a person (`dailybot login` or a personal API key) |
|
|
1012
1017
|
|
|
1013
1018
|
**Three things worth knowing before you script against this:**
|
|
1014
1019
|
|
|
1015
|
-
- **Some doors need a person.**
|
|
1016
|
-
"my inbox", can never hold
|
|
1017
|
-
|
|
1020
|
+
- **Some doors need a person.** A login session or a personal API key is a person; an agent
|
|
1021
|
+
or organization key has no answer for "my tasks" or "my inbox", can never hold
|
|
1022
|
+
`tasks:admin`, and cannot change membership or participants. A 404 means
|
|
1018
1023
|
not visible (wrong id, private without a grant, or another org) — never "not allowed".
|
|
1019
1024
|
- **Roll-ups are opt-in.** A field you did not request with `--include` is **absent** from
|
|
1020
1025
|
the payload. Absent, `null` and `0` are three different answers, and the CLI renders them
|
|
@@ -866,11 +866,13 @@ Replies to agent emails land as messages retrievable via `dailybot agent message
|
|
|
866
866
|
Projects, boards and tasks. Two groups: **`dailybot tasks`** answers questions about the
|
|
867
867
|
workspace, **`dailybot task`** reads or changes one task.
|
|
868
868
|
|
|
869
|
-
**
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
869
|
+
**Your personal API key is you.** A login session (`dailybot login`) and a personal API key
|
|
870
|
+
(one bound to you) can do everything you can do in the web app: create, update, archive and
|
|
871
|
+
restore boards, columns, projects, goals and milestones, manage board and project
|
|
872
|
+
membership, link goals to projects, and work every task. That is every non-guest member, no
|
|
873
|
+
organization-admin prerequisite. Only an agent or organization key, with nobody behind it,
|
|
874
|
+
cannot act as a person: the server refuses it (`actor_required` exit 3, `insufficient_scope`
|
|
875
|
+
exit 4). Guests stay limited by their role.
|
|
874
876
|
**Privacy is membership**, not org role: a `members` project or board is 404 (not visible)
|
|
875
877
|
to anyone without a grant; invite a person or a team to close it.
|
|
876
878
|
A board inside a `members` project follows the project's membership: whoever the project
|
|
@@ -915,9 +917,9 @@ humans who own them.
|
|
|
915
917
|
| `dailybot task comments <uuid>` | List a task's comments |
|
|
916
918
|
| `dailybot task link <a> <b> --type <rel>` | Relate two tasks (`blocks`, `relates_to`, `duplicates`) |
|
|
917
919
|
| `dailybot task labels <uuid> --mode add\|remove\|replace` | Change a task's labels |
|
|
918
|
-
| `dailybot task participants add\|list\|remove <task>` | Who is on a task (`--role participant\|watcher`); `
|
|
920
|
+
| `dailybot task participants add\|list\|remove <task>` | Who is on a task (`--role participant\|watcher`); all need a person: `dailybot login` or a personal API key |
|
|
919
921
|
| `dailybot task watch\|unwatch <task>` | Follow a task privately — **needs a person: `dailybot login` or a personal API key** |
|
|
920
|
-
| `dailybot task mute\|unmute <task>` | Silence a task and stay on it (leaving is `participants remove`) — **needs `dailybot login
|
|
922
|
+
| `dailybot task mute\|unmute <task>` | Silence a task and stay on it (leaving is `participants remove`) — **needs a person: `dailybot login` or a personal API key** |
|
|
921
923
|
| `dailybot task relations <task>` · `task unlink <task> <relation>` | List links (direction included) / remove one; `unlink` confirms and has `--dry-run` |
|
|
922
924
|
| `dailybot task comment-edit\|comment-delete <task> <comment>` | Edit (`-` reads stdin) or delete a comment; delete blanks the text and keeps the entry |
|
|
923
925
|
| `dailybot task children <task>` · `task events <task>` · `task activity <task>` | Sub-tasks, raw event history, and the readable activity feed (`--updated-since`, `--type`) |
|
|
@@ -939,43 +941,46 @@ humans who own them.
|
|
|
939
941
|
| `dailybot board labels <uuid>` | Labels available on the board — **needs a person: `dailybot login` or a personal API key** |
|
|
940
942
|
| `dailybot board views <uuid>` | Your saved views on the board, plus the ETag a save needs (`--etag` prints only that) |
|
|
941
943
|
| `dailybot board state create\|update\|archive\|restore\|reorder` | Manage columns. `archive` previews first and takes `--migrate-to <state>` to move the column's cards |
|
|
942
|
-
| `dailybot board member add <board> <user>` (or `--team <team>`) · `board member remove <board> <user>` | Who can see the board — **needs `dailybot login
|
|
944
|
+
| `dailybot board member add <board> <user>` (or `--team <team>`) · `board member remove <board> <user>` | Who can see the board — **needs a person: `dailybot login` or a personal API key**. Membership is the privacy control (not org role). A team grant follows the team live. Last grant on a private board stays (`last_grant_cannot_be_removed`) |
|
|
943
945
|
| `dailybot board label create <board> -n <name>` | Create an organization label from the board — **needs a person: `dailybot login` or a personal API key** |
|
|
944
946
|
| `dailybot board view save <board> -f views.json --if-match <etag>` | Replace your saved views (the whole list) — **needs a person: `dailybot login` or a personal API key** |
|
|
945
947
|
| `dailybot board snapshot <uuid>` | The whole board in one request; carries the `delta_cursor` that `tasks changes` consumes |
|
|
946
948
|
| `dailybot board update <uuid>` | Name, key (the old key stays reserved), visibility, estimate scale, auto-archive, project |
|
|
947
|
-
| `dailybot board create --name <n> --project <uuid> --key <KEY>` | Create a board in a project; the key prefixes its tasks (`DSN-1`) — **needs `dailybot login
|
|
949
|
+
| `dailybot board create --name <n> --project <uuid> --key <KEY>` | Create a board in a project; the key prefixes its tasks (`DSN-1`) — **needs a person: `dailybot login` or a personal API key** (any non-guest member) |
|
|
948
950
|
| `dailybot board archive <uuid>` | Archive a board. **Cascade-archives its live tasks**, and restoring does not bring them back |
|
|
949
951
|
| `dailybot board restore <uuid>` | Restore a board (cascaded tasks stay archived) |
|
|
950
952
|
| `dailybot project list` | List projects (`--include progress`) |
|
|
951
953
|
| `dailybot project get <uuid>` | Show one project |
|
|
952
954
|
| `dailybot project updates [<project>]` | Batched update digest, or one project's feed |
|
|
953
955
|
| `dailybot project update-post <uuid> <body>` | **Post a project update** — how the team sees what was done (`--health`; sends an idempotency key) |
|
|
956
|
+
| `dailybot project update-get\|update-edit\|update-delete <project> <update>` | Read one update (author, `via <agent>`, health, files); edit its text or health (author only); delete it (author or org admin; `--dry-run`) |
|
|
957
|
+
| `dailybot project update-attach\|update-attachments <project> <update>` · `update-attachment get\|rename\|delete` | Files on an update (≤5 MiB). Attach and rename: author only; delete: author or an organization admin. For an inline image, attach it, then `update-edit` the body with `attachment:<uuid>` |
|
|
954
958
|
| `dailybot project milestones [<uuid>]` | List milestones |
|
|
955
959
|
| `dailybot project milestone-complete <p> <m>` | Complete a milestone. **Its open tasks stay open** |
|
|
956
960
|
| `dailybot project milestone-reopen <p> <m>` | Reopen a milestone |
|
|
957
|
-
| `dailybot project create --name <n>` | Create a project — **needs `dailybot login
|
|
961
|
+
| `dailybot project create --name <n>` | Create a project — **needs a person: `dailybot login` or a personal API key** |
|
|
958
962
|
| `dailybot project update <uuid>` | Name, description, lead, health, dates, visibility |
|
|
959
963
|
| `dailybot project restore <uuid>` | Restore an archived project (its boards and tasks stay archived) |
|
|
960
|
-
| `dailybot project members <uuid>` · `project member add\|remove` | Who can see the project — people or whole teams (`--team`) — `members` works with a personal API key; `member add\|remove` **needs `dailybot login
|
|
961
|
-
| `dailybot project views <uuid>` · `project view save` | Your saved views; save replaces the list and requires the ETag — **needs `dailybot login
|
|
964
|
+
| `dailybot project members <uuid>` · `project member add\|remove` | Who can see the project — people or whole teams (`--team`) — `members` works with a personal API key; `member add\|remove` **needs a person: `dailybot login` or a personal API key** |
|
|
965
|
+
| `dailybot project views <uuid>` · `project view save` | Your saved views; save replaces the list and requires the ETag — **needs a person: `dailybot login` or a personal API key** |
|
|
962
966
|
| `dailybot project milestone-create\|milestone-update\|milestone-delete` | Dated milestones; delete retires it (tasks keep pointing at it) |
|
|
967
|
+
| `dailybot project milestone-attach\|milestone-attachments <project> <milestone>` · `milestone-attachment get\|rename\|delete` | Files on a milestone (≤5 MiB); reference one in the description with `attachment:<uuid>`. `project milestone-restore` brings a retired milestone back |
|
|
963
968
|
| `dailybot project archive <uuid>` | Archive a project |
|
|
964
|
-
| `dailybot project attach <uuid> <file>` · `project attachments` · `project attachment get\|delete` | Files on a project (≤5 MiB, one request). Attaching and deleting need `dailybot login` |
|
|
969
|
+
| `dailybot project attach <uuid> <file>` · `project attachments` · `project attachment get\|delete` | Files on a project (≤5 MiB, one request). Attaching and deleting need a person (`dailybot login` or a personal API key) |
|
|
965
970
|
| `dailybot goal list` | List goals (`--include` is repeatable: `--include progress --include projects`) |
|
|
966
971
|
| `dailybot goal get <uuid>` | Show one goal, with its progress and linked projects (always included) |
|
|
967
|
-
| `dailybot goal create --name <n> --period-start <d> --period-end <d>` | Create a goal (a dated commitment) — **needs `dailybot login
|
|
972
|
+
| `dailybot goal create --name <n> --period-start <d> --period-end <d>` | Create a goal (a dated commitment) — **needs a person: `dailybot login` or a personal API key** |
|
|
968
973
|
| `dailybot goal update <uuid>` | Name, period, owner, team, and the declared `--status` (not_started, on_track, at_risk, off_track, achieved, missed) |
|
|
969
974
|
| `dailybot goal restore <uuid>` | Restore an archived goal |
|
|
970
975
|
| `dailybot goal link\|unlink <goal> <project>` | Make a project count toward a goal (or stop it) |
|
|
971
976
|
| `dailybot goal archive <uuid>` | Archive a goal (its projects are not archived) |
|
|
972
|
-
| `dailybot goal attach <uuid> <file>` · `goal attachments` · `goal attachment get\|delete` | Files on a goal (≤5 MiB, one request). Attaching and deleting need `dailybot login` |
|
|
977
|
+
| `dailybot goal attach <uuid> <file>` · `goal attachments` · `goal attachment get\|delete` | Files on a goal (≤5 MiB, one request). Attaching and deleting need a person (`dailybot login` or a personal API key) |
|
|
973
978
|
|
|
974
979
|
**Three things worth knowing before you script against this:**
|
|
975
980
|
|
|
976
|
-
- **Some doors need a person.**
|
|
977
|
-
"my inbox", can never hold
|
|
978
|
-
|
|
981
|
+
- **Some doors need a person.** A login session or a personal API key is a person; an agent
|
|
982
|
+
or organization key has no answer for "my tasks" or "my inbox", can never hold
|
|
983
|
+
`tasks:admin`, and cannot change membership or participants. A 404 means
|
|
979
984
|
not visible (wrong id, private without a grant, or another org) — never "not allowed".
|
|
980
985
|
- **Roll-ups are opt-in.** A field you did not request with `--include` is **absent** from
|
|
981
986
|
the payload. Absent, `null` and `0` are three different answers, and the CLI renders them
|
|
@@ -100,6 +100,8 @@ SAME_ORIGIN_UPLOAD_METHODS: frozenset[str] = frozenset({"PUT", "POST"})
|
|
|
100
100
|
# BLAST_RADIUS.md records this as THE volume guard for unattended destructive
|
|
101
101
|
# loops — the CLI adds no second ceiling of its own.
|
|
102
102
|
TASKS_BULK_MAX_ITEMS: int = 100
|
|
103
|
+
# Body field an attachment rename (PATCH …/attachments/<uuid>/) carries.
|
|
104
|
+
ATTACHMENT_RENAME_FIELD: str = "filename"
|
|
103
105
|
# Collections the task detail door can embed in one read (first page each).
|
|
104
106
|
TASK_BRIEFING_INCLUDES: tuple[str, ...] = (
|
|
105
107
|
"relations",
|
|
@@ -2885,7 +2887,7 @@ class DailyBotClient:
|
|
|
2885
2887
|
return self._tasks_write(
|
|
2886
2888
|
"POST",
|
|
2887
2889
|
f"tasks/{_path_segment(task_uuid)}/labels/batch/",
|
|
2888
|
-
json={"mode": mode, "
|
|
2890
|
+
json={"mode": mode, "label_uuids": labels},
|
|
2889
2891
|
idempotent=True,
|
|
2890
2892
|
idempotency_key=idempotency_key,
|
|
2891
2893
|
)
|
|
@@ -3321,6 +3323,143 @@ class DailyBotClient:
|
|
|
3321
3323
|
"""GET /v1/tasks/projects/<p>/attachments/<a>/content/."""
|
|
3322
3324
|
return self._download_attachment_of(self._project_parent(project_uuid), attachment_uuid)
|
|
3323
3325
|
|
|
3326
|
+
# Milestone and project-update attachments: the same row shape as project ones.
|
|
3327
|
+
@staticmethod
|
|
3328
|
+
def _milestone_parent(project_uuid: str, milestone_uuid: str) -> str:
|
|
3329
|
+
return f"projects/{_path_segment(project_uuid)}/milestones/{_path_segment(milestone_uuid)}"
|
|
3330
|
+
|
|
3331
|
+
@staticmethod
|
|
3332
|
+
def _update_parent(project_uuid: str, update_uuid: str) -> str:
|
|
3333
|
+
return f"projects/{_path_segment(project_uuid)}/updates/{_path_segment(update_uuid)}"
|
|
3334
|
+
|
|
3335
|
+
def _rename_attachment_of(self, parent: str, attachment_uuid: str, *, filename: str) -> Any:
|
|
3336
|
+
"""PATCH <parent>/attachments/<uuid>/ — rename the file."""
|
|
3337
|
+
return self._tasks_write(
|
|
3338
|
+
"PATCH",
|
|
3339
|
+
f"{parent}/attachments/{_path_segment(attachment_uuid)}/",
|
|
3340
|
+
json={ATTACHMENT_RENAME_FIELD: filename},
|
|
3341
|
+
)
|
|
3342
|
+
|
|
3343
|
+
def upload_milestone_attachment(
|
|
3344
|
+
self,
|
|
3345
|
+
project_uuid: str,
|
|
3346
|
+
milestone_uuid: str,
|
|
3347
|
+
*,
|
|
3348
|
+
filename: str,
|
|
3349
|
+
content_type: str,
|
|
3350
|
+
data: bytes,
|
|
3351
|
+
caption: str | None = None,
|
|
3352
|
+
) -> dict[str, Any]:
|
|
3353
|
+
"""POST …/milestones/<m>/attachments/ as multipart (≤5 MiB)."""
|
|
3354
|
+
return self._upload_attachment_to(
|
|
3355
|
+
self._milestone_parent(project_uuid, milestone_uuid),
|
|
3356
|
+
filename=filename,
|
|
3357
|
+
content_type=content_type,
|
|
3358
|
+
data=data,
|
|
3359
|
+
caption=caption,
|
|
3360
|
+
)
|
|
3361
|
+
|
|
3362
|
+
def list_milestone_attachments(self, project_uuid: str, milestone_uuid: str) -> Any:
|
|
3363
|
+
"""GET …/milestones/<m>/attachments/."""
|
|
3364
|
+
return self._list_attachments_of(self._milestone_parent(project_uuid, milestone_uuid))
|
|
3365
|
+
|
|
3366
|
+
def download_milestone_attachment(
|
|
3367
|
+
self, project_uuid: str, milestone_uuid: str, attachment_uuid: str
|
|
3368
|
+
) -> bytes:
|
|
3369
|
+
"""GET …/milestones/<m>/attachments/<a>/content/."""
|
|
3370
|
+
return self._download_attachment_of(
|
|
3371
|
+
self._milestone_parent(project_uuid, milestone_uuid), attachment_uuid
|
|
3372
|
+
)
|
|
3373
|
+
|
|
3374
|
+
def rename_milestone_attachment(
|
|
3375
|
+
self, project_uuid: str, milestone_uuid: str, attachment_uuid: str, *, filename: str
|
|
3376
|
+
) -> Any:
|
|
3377
|
+
"""PATCH …/milestones/<m>/attachments/<a>/."""
|
|
3378
|
+
return self._rename_attachment_of(
|
|
3379
|
+
self._milestone_parent(project_uuid, milestone_uuid), attachment_uuid, filename=filename
|
|
3380
|
+
)
|
|
3381
|
+
|
|
3382
|
+
def delete_milestone_attachment(
|
|
3383
|
+
self, project_uuid: str, milestone_uuid: str, attachment_uuid: str
|
|
3384
|
+
) -> Any:
|
|
3385
|
+
"""DELETE …/milestones/<m>/attachments/<a>/."""
|
|
3386
|
+
return self._delete_attachment_of(
|
|
3387
|
+
self._milestone_parent(project_uuid, milestone_uuid), attachment_uuid
|
|
3388
|
+
)
|
|
3389
|
+
|
|
3390
|
+
def get_project_update(self, project_uuid: str, update_uuid: str) -> dict[str, Any]:
|
|
3391
|
+
"""GET /v1/tasks/projects/<p>/updates/<u>/."""
|
|
3392
|
+
return self._tasks_read(f"{self._update_parent(project_uuid, update_uuid)}/")
|
|
3393
|
+
|
|
3394
|
+
def edit_project_update(
|
|
3395
|
+
self,
|
|
3396
|
+
project_uuid: str,
|
|
3397
|
+
update_uuid: str,
|
|
3398
|
+
*,
|
|
3399
|
+
body: str | None = None,
|
|
3400
|
+
health: str | None = None,
|
|
3401
|
+
) -> Any:
|
|
3402
|
+
"""PATCH /v1/tasks/projects/<p>/updates/<u>/ — author only (`update_not_author`)."""
|
|
3403
|
+
payload: dict[str, Any] = {}
|
|
3404
|
+
if body is not None:
|
|
3405
|
+
payload["body"] = body
|
|
3406
|
+
if health is not None:
|
|
3407
|
+
payload["health"] = health
|
|
3408
|
+
return self._tasks_write(
|
|
3409
|
+
"PATCH", f"{self._update_parent(project_uuid, update_uuid)}/", json=payload
|
|
3410
|
+
)
|
|
3411
|
+
|
|
3412
|
+
def delete_project_update(self, project_uuid: str, update_uuid: str) -> Any:
|
|
3413
|
+
"""DELETE /v1/tasks/projects/<p>/updates/<u>/ — the author or an org admin."""
|
|
3414
|
+
return self._tasks_write("DELETE", f"{self._update_parent(project_uuid, update_uuid)}/")
|
|
3415
|
+
|
|
3416
|
+
def upload_update_attachment(
|
|
3417
|
+
self,
|
|
3418
|
+
project_uuid: str,
|
|
3419
|
+
update_uuid: str,
|
|
3420
|
+
*,
|
|
3421
|
+
filename: str,
|
|
3422
|
+
content_type: str,
|
|
3423
|
+
data: bytes,
|
|
3424
|
+
caption: str | None = None,
|
|
3425
|
+
) -> dict[str, Any]:
|
|
3426
|
+
"""POST …/updates/<u>/attachments/ as multipart (≤5 MiB) — author only."""
|
|
3427
|
+
return self._upload_attachment_to(
|
|
3428
|
+
self._update_parent(project_uuid, update_uuid),
|
|
3429
|
+
filename=filename,
|
|
3430
|
+
content_type=content_type,
|
|
3431
|
+
data=data,
|
|
3432
|
+
caption=caption,
|
|
3433
|
+
)
|
|
3434
|
+
|
|
3435
|
+
def list_update_attachments(self, project_uuid: str, update_uuid: str) -> Any:
|
|
3436
|
+
"""GET …/updates/<u>/attachments/."""
|
|
3437
|
+
return self._list_attachments_of(self._update_parent(project_uuid, update_uuid))
|
|
3438
|
+
|
|
3439
|
+
def download_update_attachment(
|
|
3440
|
+
self, project_uuid: str, update_uuid: str, attachment_uuid: str
|
|
3441
|
+
) -> bytes:
|
|
3442
|
+
"""GET …/updates/<u>/attachments/<a>/content/."""
|
|
3443
|
+
return self._download_attachment_of(
|
|
3444
|
+
self._update_parent(project_uuid, update_uuid), attachment_uuid
|
|
3445
|
+
)
|
|
3446
|
+
|
|
3447
|
+
def rename_update_attachment(
|
|
3448
|
+
self, project_uuid: str, update_uuid: str, attachment_uuid: str, *, filename: str
|
|
3449
|
+
) -> Any:
|
|
3450
|
+
"""PATCH …/updates/<u>/attachments/<a>/."""
|
|
3451
|
+
return self._rename_attachment_of(
|
|
3452
|
+
self._update_parent(project_uuid, update_uuid), attachment_uuid, filename=filename
|
|
3453
|
+
)
|
|
3454
|
+
|
|
3455
|
+
def delete_update_attachment(
|
|
3456
|
+
self, project_uuid: str, update_uuid: str, attachment_uuid: str
|
|
3457
|
+
) -> Any:
|
|
3458
|
+
"""DELETE …/updates/<u>/attachments/<a>/."""
|
|
3459
|
+
return self._delete_attachment_of(
|
|
3460
|
+
self._update_parent(project_uuid, update_uuid), attachment_uuid
|
|
3461
|
+
)
|
|
3462
|
+
|
|
3324
3463
|
# Goal attachments (POST / DELETE: signed-in person; reads: anyone who can see it)
|
|
3325
3464
|
def upload_goal_attachment(
|
|
3326
3465
|
self,
|
|
@@ -3599,6 +3738,14 @@ class DailyBotClient:
|
|
|
3599
3738
|
json={k: v for k, v in fields.items() if v is not None},
|
|
3600
3739
|
)
|
|
3601
3740
|
|
|
3741
|
+
def restore_milestone(self, project_uuid: str, milestone_uuid: str) -> dict[str, Any]:
|
|
3742
|
+
"""POST …/milestones/<uuid>/restore/ — bring a retired milestone back (idempotent)."""
|
|
3743
|
+
return self._tasks_write(
|
|
3744
|
+
"POST",
|
|
3745
|
+
f"projects/{_path_segment(project_uuid)}/milestones/{_path_segment(milestone_uuid)}/"
|
|
3746
|
+
"restore/",
|
|
3747
|
+
)
|
|
3748
|
+
|
|
3602
3749
|
def delete_milestone(self, project_uuid: str, milestone_uuid: str) -> Any:
|
|
3603
3750
|
"""DELETE …/milestones/<uuid>/ — retires (archives); tasks keep pointing at it."""
|
|
3604
3751
|
return self._tasks_write(
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
"""Shared attachment flows for tasks, comments, projects and
|
|
1
|
+
"""Shared attachment flows for tasks, comments, projects, goals, milestones and updates.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Six parents own an `attachments/` collection with the same row shape, so the
|
|
4
4
|
commands share one implementation of each step: read the local file under a
|
|
5
5
|
hard limit, upload, list, download to a path that is never overwritten by
|
|
6
|
-
surprise, and delete with a stated
|
|
6
|
+
surprise, rename within the server's bounds, and delete with a stated
|
|
7
|
+
consequence. Each command supplies only the
|
|
7
8
|
client call for its parent.
|
|
8
9
|
"""
|
|
9
10
|
|
|
@@ -190,6 +191,34 @@ def run_get(
|
|
|
190
191
|
print_success(f"Saved {len(content)} bytes to {output}.")
|
|
191
192
|
|
|
192
193
|
|
|
194
|
+
ATTACHMENT_NAME_MAX_CHARS: int = 255
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def run_rename(
|
|
198
|
+
rename: Callable[[Any, str], Any],
|
|
199
|
+
filename: str,
|
|
200
|
+
*,
|
|
201
|
+
json_mode: bool,
|
|
202
|
+
require_auth: Callable[[], Any],
|
|
203
|
+
) -> None:
|
|
204
|
+
"""Rename one attachment. The name is checked locally against the server's bounds."""
|
|
205
|
+
cleaned: str = filename.strip()
|
|
206
|
+
if not cleaned or len(cleaned) > ATTACHMENT_NAME_MAX_CHARS:
|
|
207
|
+
raise click.UsageError(
|
|
208
|
+
f"The new name must be 1 to {ATTACHMENT_NAME_MAX_CHARS} characters. Nothing was sent."
|
|
209
|
+
)
|
|
210
|
+
client: Any = require_auth()
|
|
211
|
+
try:
|
|
212
|
+
with console.status("Renaming the attachment..."):
|
|
213
|
+
data: Any = rename(client, cleaned)
|
|
214
|
+
except APIError as exc:
|
|
215
|
+
exit_for_tasks_error(exc, json_mode)
|
|
216
|
+
if json_mode:
|
|
217
|
+
emit_json(data)
|
|
218
|
+
return
|
|
219
|
+
print_success(f"Attachment renamed to {cleaned}.")
|
|
220
|
+
|
|
221
|
+
|
|
193
222
|
def run_delete(
|
|
194
223
|
delete: Callable[[Any], Any],
|
|
195
224
|
consequence: str,
|
|
@@ -21,7 +21,6 @@ from dailybot_cli.commands.public_api_helpers import (
|
|
|
21
21
|
emit_json,
|
|
22
22
|
exit_for_tasks_error,
|
|
23
23
|
load_json_input,
|
|
24
|
-
refuse_without_person,
|
|
25
24
|
require_auth,
|
|
26
25
|
rows_of,
|
|
27
26
|
)
|
|
@@ -32,7 +31,6 @@ from dailybot_cli.commands.query_options import (
|
|
|
32
31
|
query_options,
|
|
33
32
|
resolve_fetch_all,
|
|
34
33
|
)
|
|
35
|
-
from dailybot_cli.config import get_person_token
|
|
36
34
|
from dailybot_cli.display import (
|
|
37
35
|
console,
|
|
38
36
|
print_board_snapshot,
|
|
@@ -479,7 +477,6 @@ def board_state_create(
|
|
|
479
477
|
dailybot board state create <board-uuid> -n "In review" --category in_progress
|
|
480
478
|
dailybot board state create <board-uuid> -n Blocked --category todo --position 2 --json
|
|
481
479
|
"""
|
|
482
|
-
_require_person_for_admin("board state create", json_mode=json_mode)
|
|
483
480
|
client = require_auth()
|
|
484
481
|
try:
|
|
485
482
|
with console.status("Adding the column..."):
|
|
@@ -528,7 +525,6 @@ def board_state_update(
|
|
|
528
525
|
dailybot board state update <board-uuid> <state-uuid> --name "Shipped"
|
|
529
526
|
dailybot board state update <board-uuid> <state-uuid> --position 1 --json
|
|
530
527
|
"""
|
|
531
|
-
_require_person_for_admin("board state update", json_mode=json_mode)
|
|
532
528
|
fields: dict[str, Any] = {
|
|
533
529
|
k: v
|
|
534
530
|
for k, v in {"name": name, "color": color, "position": position}.items()
|
|
@@ -578,7 +574,6 @@ def board_state_archive(
|
|
|
578
574
|
dailybot board state archive <board-uuid> <state-uuid> --dry-run
|
|
579
575
|
dailybot board state archive <board-uuid> <state-uuid> --migrate-to <other-state> --yes
|
|
580
576
|
"""
|
|
581
|
-
_require_person_for_admin("board state archive", json_mode=json_mode)
|
|
582
577
|
client = require_auth()
|
|
583
578
|
if not preview_then_confirm(
|
|
584
579
|
lambda: client.archive_board_state(
|
|
@@ -613,7 +608,6 @@ def board_state_restore(board_uuid: str, state_uuid: str, json_mode: bool) -> No
|
|
|
613
608
|
Examples:
|
|
614
609
|
dailybot board state restore <board-uuid> <state-uuid>
|
|
615
610
|
"""
|
|
616
|
-
_require_person_for_admin("board state restore", json_mode=json_mode)
|
|
617
611
|
client = require_auth()
|
|
618
612
|
try:
|
|
619
613
|
with console.status("Restoring the column..."):
|
|
@@ -642,7 +636,6 @@ def board_state_reorder(board_uuid: str, state_uuids: tuple[str, ...], json_mode
|
|
|
642
636
|
Examples:
|
|
643
637
|
dailybot board state reorder <board-uuid> <backlog> <todo> <doing> <done>
|
|
644
638
|
"""
|
|
645
|
-
_require_person_for_admin("board state reorder", json_mode=json_mode)
|
|
646
639
|
if len(set(state_uuids)) != len(state_uuids):
|
|
647
640
|
raise click.UsageError("A column appears twice. List each live column exactly once.")
|
|
648
641
|
client = require_auth()
|
|
@@ -669,7 +662,7 @@ _MEMBER_WRITE_REASON: str = (
|
|
|
669
662
|
|
|
670
663
|
@board.group("member")
|
|
671
664
|
def board_member() -> None:
|
|
672
|
-
"""Invite or remove who can see a board. Needs `dailybot login
|
|
665
|
+
"""Invite or remove who can see a board. Needs a person: `dailybot login` or a personal API key.
|
|
673
666
|
|
|
674
667
|
\b
|
|
675
668
|
Membership is the privacy control: a `members` board is invisible (404) to
|
|
@@ -713,7 +706,6 @@ def board_member_add(
|
|
|
713
706
|
dailybot board member add <board-uuid> <user-uuid>
|
|
714
707
|
dailybot board member add <board-uuid> --team <team-uuid> --json
|
|
715
708
|
"""
|
|
716
|
-
_require_person_for_admin("board member add", json_mode=json_mode)
|
|
717
709
|
if (user_uuid is None) == (team_uuid is None):
|
|
718
710
|
raise click.UsageError("Pass exactly one of USER or --team.")
|
|
719
711
|
client = require_auth()
|
|
@@ -751,7 +743,6 @@ def board_member_remove(
|
|
|
751
743
|
dailybot board member remove <board-uuid> <user-uuid> --dry-run
|
|
752
744
|
dailybot board member remove <board-uuid> <user-uuid> --yes
|
|
753
745
|
"""
|
|
754
|
-
_require_person_for_admin("board member remove", json_mode=json_mode)
|
|
755
746
|
if not confirm_without_preview(
|
|
756
747
|
f"remove user {user_uuid} from board {board_uuid}; they lose sight of it if it is private.",
|
|
757
748
|
assume_yes=assume_yes,
|
|
@@ -930,26 +921,6 @@ def board_snapshot(board_uuid: str, json_mode: bool) -> None:
|
|
|
930
921
|
print_board_snapshot(data)
|
|
931
922
|
|
|
932
923
|
|
|
933
|
-
def _require_person_for_admin(action: str, *, json_mode: bool) -> None:
|
|
934
|
-
"""Refuse an organization API key on a structure or membership door.
|
|
935
|
-
|
|
936
|
-
Open-org Tasks: every non-guest member holds `tasks:admin` on a person session,
|
|
937
|
-
so the CLI never asks for an organization-admin role. An organization API key
|
|
938
|
-
still cannot store that scope (or change membership / participants), so the
|
|
939
|
-
message blames the **credential kind** — run `dailybot login` as a member.
|
|
940
|
-
"""
|
|
941
|
-
# Gate on the absence of a person token, not on the presence of a key: both
|
|
942
|
-
# can be configured at once, and Bearer is sent first when it exists.
|
|
943
|
-
if get_person_token() is None:
|
|
944
|
-
refuse_without_person(
|
|
945
|
-
f"`{action}` needs a signed-in person. An organization API key can never hold "
|
|
946
|
-
"the `tasks:admin` scope (it cannot even be stored on one), and keys cannot "
|
|
947
|
-
"change membership. Run `dailybot login` and retry as a non-guest member.",
|
|
948
|
-
json_mode=json_mode,
|
|
949
|
-
admin=True,
|
|
950
|
-
)
|
|
951
|
-
|
|
952
|
-
|
|
953
924
|
@board.command("create")
|
|
954
925
|
@click.option("-n", "--name", required=True, help="Board name.")
|
|
955
926
|
# Boards have no description field (BoardWrite declares none), so the flag the CLI
|
|
@@ -977,11 +948,11 @@ def board_create(
|
|
|
977
948
|
idempotency_key: str | None,
|
|
978
949
|
json_mode: bool,
|
|
979
950
|
) -> None:
|
|
980
|
-
"""Create a board in a project. Needs a
|
|
951
|
+
"""Create a board in a project. Needs a person (any non-guest member): `dailybot login` or a personal API key.
|
|
981
952
|
|
|
982
953
|
\b
|
|
983
|
-
An organization
|
|
984
|
-
|
|
954
|
+
An agent or organization key cannot do this. Visibility and who can see the
|
|
955
|
+
board are controlled by invite/remove, not by org role.
|
|
985
956
|
|
|
986
957
|
\b
|
|
987
958
|
Examples:
|
|
@@ -992,7 +963,6 @@ def board_create(
|
|
|
992
963
|
"Boards have no description. Put the context in a project "
|
|
993
964
|
"(`dailybot project create -d ...`) and create the board under it."
|
|
994
965
|
)
|
|
995
|
-
_require_person_for_admin("board create", json_mode=json_mode)
|
|
996
966
|
client = require_auth()
|
|
997
967
|
try:
|
|
998
968
|
with console.status("Creating the board..."):
|
|
@@ -1054,7 +1024,6 @@ def board_update(
|
|
|
1054
1024
|
dailybot board update <board-uuid> --name "Design (Q4)"
|
|
1055
1025
|
dailybot board update <board-uuid> --key DSN --visibility members --json
|
|
1056
1026
|
"""
|
|
1057
|
-
_require_person_for_admin("board update", json_mode=json_mode)
|
|
1058
1027
|
fields: dict[str, Any] = {
|
|
1059
1028
|
k: v
|
|
1060
1029
|
for k, v in {
|
|
@@ -1105,7 +1074,6 @@ def board_archive(
|
|
|
1105
1074
|
Examples:
|
|
1106
1075
|
dailybot board archive <board-uuid> --dry-run
|
|
1107
1076
|
"""
|
|
1108
|
-
_require_person_for_admin("board archive", json_mode=json_mode)
|
|
1109
1077
|
client = require_auth()
|
|
1110
1078
|
if not preview_then_confirm(
|
|
1111
1079
|
lambda: client.archive_board(board_uuid, dry_run=True),
|
|
@@ -1146,7 +1114,6 @@ def board_restore(board_uuid: str, idempotency_key: str | None, json_mode: bool)
|
|
|
1146
1114
|
Examples:
|
|
1147
1115
|
dailybot board restore <board-uuid>
|
|
1148
1116
|
"""
|
|
1149
|
-
_require_person_for_admin("board restore", json_mode=json_mode)
|
|
1150
1117
|
client = require_auth()
|
|
1151
1118
|
try:
|
|
1152
1119
|
with console.status("Restoring the board..."):
|