teams-cli 0.1.1__tar.gz → 0.1.3__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.
- teams_cli-0.1.3/.github/workflows/ci.yml +54 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/.github/workflows/release.yml +78 -25
- teams_cli-0.1.3/.mise.toml +22 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/CHANGELOG.md +25 -3
- {teams_cli-0.1.1 → teams_cli-0.1.3}/Cargo.lock +1 -1
- {teams_cli-0.1.1 → teams_cli-0.1.3}/Cargo.toml +2 -2
- {teams_cli-0.1.1 → teams_cli-0.1.3}/Makefile +10 -1
- {teams_cli-0.1.1 → teams_cli-0.1.3}/PKG-INFO +43 -8
- {teams_cli-0.1.1 → teams_cli-0.1.3}/PRODUCT.md +2 -1
- {teams_cli-0.1.1 → teams_cli-0.1.3}/README.md +42 -7
- teams_cli-0.1.3/docs/releases.md +38 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/auth.rs +67 -9
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/cli.rs +24 -1
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/config.rs +53 -4
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/error.rs +16 -6
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/graph.rs +40 -3
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/main.rs +158 -59
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/output.rs +2 -2
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/schema.rs +64 -6
- {teams_cli-0.1.1 → teams_cli-0.1.3}/tests/cli.rs +85 -11
- teams_cli-0.1.3/vership.toml +10 -0
- teams_cli-0.1.1/.github/workflows/ci.yml +0 -47
- {teams_cli-0.1.1 → teams_cli-0.1.3}/.github/dependabot.yml +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/.gitignore +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/DESIGN.md +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/LICENSE +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/examples/validate_schema.rs +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/pyproject.toml +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/lib.rs +0 -0
- {teams_cli-0.1.1 → teams_cli-0.1.3}/src/tui.rs +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Test (${{ matrix.os }})
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
21
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
22
|
+
with:
|
|
23
|
+
toolchain: 1.96.0
|
|
24
|
+
components: rustfmt, clippy
|
|
25
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6
|
|
26
|
+
- run: cargo test --locked --all-targets
|
|
27
|
+
|
|
28
|
+
quality:
|
|
29
|
+
name: Formatting and lints
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
33
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
34
|
+
with:
|
|
35
|
+
toolchain: 1.96.0
|
|
36
|
+
components: rustfmt, clippy
|
|
37
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6
|
|
38
|
+
- run: cargo fmt -- --check
|
|
39
|
+
- run: cargo clippy --locked --all-targets -- -D warnings
|
|
40
|
+
|
|
41
|
+
security:
|
|
42
|
+
name: Dependency audit
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
46
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
47
|
+
with:
|
|
48
|
+
toolchain: 1.96.0
|
|
49
|
+
components: rustfmt, clippy
|
|
50
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6
|
|
51
|
+
- name: Install cargo-audit
|
|
52
|
+
run: cargo install cargo-audit --version 0.22.2 --locked
|
|
53
|
+
- name: Audit Cargo.lock
|
|
54
|
+
run: cargo audit
|
|
@@ -3,6 +3,17 @@ name: Release
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
tags: ["v[0-9]*.[0-9]*.[0-9]*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
version:
|
|
9
|
+
description: "Version to rehearse (defaults to Cargo.toml)"
|
|
10
|
+
required: false
|
|
11
|
+
type: string
|
|
12
|
+
dry_run:
|
|
13
|
+
description: "Build and validate without publishing"
|
|
14
|
+
required: true
|
|
15
|
+
default: true
|
|
16
|
+
type: boolean
|
|
6
17
|
|
|
7
18
|
permissions:
|
|
8
19
|
contents: read
|
|
@@ -12,11 +23,23 @@ jobs:
|
|
|
12
23
|
name: Verify release version
|
|
13
24
|
runs-on: ubuntu-latest
|
|
14
25
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
16
27
|
- name: Match tag to package version
|
|
28
|
+
env:
|
|
29
|
+
REQUESTED_VERSION: ${{ inputs.version }}
|
|
30
|
+
DRY_RUN: ${{ inputs.dry_run }}
|
|
17
31
|
run: |
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
manifest_version="$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)"
|
|
33
|
+
version="${REQUESTED_VERSION#v}"
|
|
34
|
+
version="${version:-$manifest_version}"
|
|
35
|
+
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]
|
|
36
|
+
test "$version" = "$manifest_version"
|
|
37
|
+
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
|
|
38
|
+
test "v${version}" = "$GITHUB_REF_NAME"
|
|
39
|
+
elif [[ "$DRY_RUN" != "true" ]]; then
|
|
40
|
+
test "$GITHUB_REF_TYPE" = "tag"
|
|
41
|
+
test "v${version}" = "$GITHUB_REF_NAME"
|
|
42
|
+
fi
|
|
20
43
|
|
|
21
44
|
verify:
|
|
22
45
|
uses: ./.github/workflows/ci.yml
|
|
@@ -46,11 +69,13 @@ jobs:
|
|
|
46
69
|
id-token: write
|
|
47
70
|
attestations: write
|
|
48
71
|
steps:
|
|
49
|
-
- uses: actions/checkout@
|
|
50
|
-
- uses: dtolnay/rust-toolchain@
|
|
72
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
73
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
51
74
|
with:
|
|
75
|
+
toolchain: 1.96.0
|
|
76
|
+
components: rustfmt, clippy
|
|
52
77
|
targets: ${{ matrix.target }}
|
|
53
|
-
- uses: Swatinem/rust-cache@
|
|
78
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6
|
|
54
79
|
with:
|
|
55
80
|
key: ${{ matrix.target }}
|
|
56
81
|
- run: cargo build --locked --release --target ${{ matrix.target }}
|
|
@@ -63,10 +88,11 @@ jobs:
|
|
|
63
88
|
tar -C dist/package -czf "dist/teams-cli-${GITHUB_REF_NAME}-${{ matrix.target }}.tar.gz" .
|
|
64
89
|
rm -rf dist/package
|
|
65
90
|
- name: Attest build provenance
|
|
66
|
-
|
|
91
|
+
if: ${{ inputs.dry_run != true }}
|
|
92
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6
|
|
67
93
|
with:
|
|
68
94
|
subject-path: dist/*.tar.gz
|
|
69
|
-
- uses: actions/upload-artifact@
|
|
95
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
|
70
96
|
with:
|
|
71
97
|
name: binary-${{ matrix.target }}
|
|
72
98
|
path: dist/*.tar.gz
|
|
@@ -93,7 +119,7 @@ jobs:
|
|
|
93
119
|
manylinux: auto
|
|
94
120
|
runs-on: ${{ matrix.os }}
|
|
95
121
|
steps:
|
|
96
|
-
- uses: actions/checkout@
|
|
122
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
97
123
|
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
98
124
|
with:
|
|
99
125
|
maturin-version: v1.14.1
|
|
@@ -101,7 +127,12 @@ jobs:
|
|
|
101
127
|
target: ${{ matrix.target }}
|
|
102
128
|
manylinux: ${{ matrix.manylinux }}
|
|
103
129
|
args: --release --locked --compatibility pypi --out dist
|
|
104
|
-
- uses:
|
|
130
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
|
|
131
|
+
if: ${{ inputs.dry_run == true }}
|
|
132
|
+
- name: Validate wheel metadata
|
|
133
|
+
if: ${{ inputs.dry_run == true }}
|
|
134
|
+
run: uvx --from twine==6.2.0 twine check --strict dist/*.whl
|
|
135
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
|
105
136
|
with:
|
|
106
137
|
name: wheel-${{ matrix.target }}
|
|
107
138
|
path: dist/*.whl
|
|
@@ -112,16 +143,27 @@ jobs:
|
|
|
112
143
|
needs: [version, verify]
|
|
113
144
|
runs-on: ubuntu-latest
|
|
114
145
|
steps:
|
|
115
|
-
- uses: actions/checkout@
|
|
116
|
-
- uses: dtolnay/rust-toolchain@
|
|
146
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
147
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
148
|
+
with:
|
|
149
|
+
toolchain: 1.96.0
|
|
150
|
+
components: rustfmt, clippy
|
|
117
151
|
- run: cargo package --locked
|
|
152
|
+
- name: Validate crates.io package
|
|
153
|
+
if: ${{ inputs.dry_run == true }}
|
|
154
|
+
run: cargo publish --locked --dry-run
|
|
118
155
|
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
|
|
119
156
|
with:
|
|
120
157
|
maturin-version: v1.14.1
|
|
121
158
|
command: sdist
|
|
122
159
|
args: --out dist
|
|
123
160
|
- run: cp target/package/*.crate dist/
|
|
124
|
-
- uses:
|
|
161
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
|
|
162
|
+
if: ${{ inputs.dry_run == true }}
|
|
163
|
+
- name: Validate source distribution metadata
|
|
164
|
+
if: ${{ inputs.dry_run == true }}
|
|
165
|
+
run: uvx --from twine==6.2.0 twine check --strict dist/*.tar.gz
|
|
166
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
|
125
167
|
with:
|
|
126
168
|
name: source-distributions
|
|
127
169
|
path: dist/*
|
|
@@ -136,13 +178,13 @@ jobs:
|
|
|
136
178
|
id-token: write
|
|
137
179
|
attestations: write
|
|
138
180
|
steps:
|
|
139
|
-
- uses: actions/checkout@
|
|
140
|
-
- uses: actions/download-artifact@
|
|
181
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
182
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
|
141
183
|
with:
|
|
142
184
|
pattern: binary-*
|
|
143
185
|
path: dist
|
|
144
186
|
merge-multiple: true
|
|
145
|
-
- uses: actions/download-artifact@
|
|
187
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
|
146
188
|
with:
|
|
147
189
|
name: source-distributions
|
|
148
190
|
path: dist
|
|
@@ -155,11 +197,13 @@ jobs:
|
|
|
155
197
|
working-directory: dist
|
|
156
198
|
run: sha256sum ./*.tar.gz ./*.crate ./*.json > SHA256SUMS
|
|
157
199
|
- name: Attest artifacts and SBOM
|
|
158
|
-
|
|
200
|
+
if: ${{ inputs.dry_run != true }}
|
|
201
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6
|
|
159
202
|
with:
|
|
160
203
|
subject-path: dist/*
|
|
161
204
|
sbom-path: dist/teams-cli.cdx.json
|
|
162
205
|
- name: Create GitHub release
|
|
206
|
+
if: ${{ inputs.dry_run != true }}
|
|
163
207
|
env:
|
|
164
208
|
GH_TOKEN: ${{ github.token }}
|
|
165
209
|
run: gh release create "$GITHUB_REF_NAME" dist/* --verify-tag --generate-notes
|
|
@@ -167,13 +211,17 @@ jobs:
|
|
|
167
211
|
crates-io:
|
|
168
212
|
name: Publish crates.io
|
|
169
213
|
needs: github-release
|
|
214
|
+
if: ${{ inputs.dry_run != true }}
|
|
170
215
|
runs-on: ubuntu-latest
|
|
171
216
|
environment: crates-io
|
|
172
217
|
permissions:
|
|
173
218
|
id-token: write
|
|
174
219
|
steps:
|
|
175
|
-
- uses: actions/checkout@
|
|
176
|
-
- uses: dtolnay/rust-toolchain@
|
|
220
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
221
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
222
|
+
with:
|
|
223
|
+
toolchain: 1.96.0
|
|
224
|
+
components: rustfmt, clippy
|
|
177
225
|
- name: Check whether this version is already published
|
|
178
226
|
id: crate
|
|
179
227
|
shell: bash
|
|
@@ -191,7 +239,7 @@ jobs:
|
|
|
191
239
|
- name: Get temporary crates.io token
|
|
192
240
|
if: steps.crate.outputs.exists != 'true'
|
|
193
241
|
id: crates-auth
|
|
194
|
-
uses: rust-lang/crates-io-auth-action@
|
|
242
|
+
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18
|
|
195
243
|
- if: steps.crate.outputs.exists != 'true'
|
|
196
244
|
run: cargo publish --locked
|
|
197
245
|
env:
|
|
@@ -202,6 +250,7 @@ jobs:
|
|
|
202
250
|
pypi:
|
|
203
251
|
name: Publish PyPI
|
|
204
252
|
needs: github-release
|
|
253
|
+
if: ${{ inputs.dry_run != true }}
|
|
205
254
|
runs-on: ubuntu-latest
|
|
206
255
|
environment:
|
|
207
256
|
name: pypi
|
|
@@ -209,28 +258,32 @@ jobs:
|
|
|
209
258
|
permissions:
|
|
210
259
|
id-token: write
|
|
211
260
|
steps:
|
|
212
|
-
- uses: actions/download-artifact@
|
|
261
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
|
213
262
|
with:
|
|
214
263
|
pattern: wheel-*
|
|
215
264
|
path: dist
|
|
216
265
|
merge-multiple: true
|
|
217
|
-
- uses: actions/download-artifact@
|
|
266
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
|
218
267
|
with:
|
|
219
268
|
name: source-distributions
|
|
220
269
|
path: source
|
|
221
270
|
- name: Collect Python distributions
|
|
222
271
|
run: find source -name '*.tar.gz' -exec cp {} dist/ \;
|
|
223
|
-
- uses: pypa/gh-action-pypi-publish@
|
|
272
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
|
|
224
273
|
with:
|
|
225
274
|
packages-dir: dist
|
|
226
275
|
|
|
227
276
|
post-publish-smoke:
|
|
228
277
|
name: Verify public packages
|
|
229
278
|
needs: [crates-io, pypi]
|
|
279
|
+
if: ${{ inputs.dry_run != true }}
|
|
230
280
|
runs-on: ubuntu-latest
|
|
231
281
|
steps:
|
|
232
|
-
- uses: dtolnay/rust-toolchain@
|
|
233
|
-
|
|
282
|
+
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
|
|
283
|
+
with:
|
|
284
|
+
toolchain: 1.96.0
|
|
285
|
+
components: rustfmt, clippy
|
|
286
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
|
|
234
287
|
with:
|
|
235
288
|
python-version: "3.x"
|
|
236
289
|
- name: Install and run the crates.io package
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Reproducible local and CI tooling.
|
|
2
|
+
min_version = "2026.4.19"
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
RUST_BACKTRACE = "1"
|
|
6
|
+
BINSTALL_DISABLE_TELEMETRY = "true"
|
|
7
|
+
|
|
8
|
+
[tools]
|
|
9
|
+
rust = "1.96.0"
|
|
10
|
+
actionlint = "1.7.12"
|
|
11
|
+
cargo-binstall = "1.22.0"
|
|
12
|
+
"cargo:cargo-nextest" = "0.9.143"
|
|
13
|
+
|
|
14
|
+
[settings]
|
|
15
|
+
cargo.binstall = true
|
|
16
|
+
|
|
17
|
+
[tasks.setup]
|
|
18
|
+
description = "Install the exact development toolchain"
|
|
19
|
+
run = """
|
|
20
|
+
mise install rust actionlint cargo-binstall
|
|
21
|
+
mise install cargo:cargo-nextest
|
|
22
|
+
"""
|
|
@@ -5,6 +5,31 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.3](https://github.com/rvben/teams-cli/compare/v0.1.2...v0.1.3) - 2026-08-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **cli**: align safety and configuration contract ([c564f63](https://github.com/rvben/teams-cli/commit/c564f63f7deeb4e8a7c8faa359e32f3ea0da8556))
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **config**: persist profiles atomically ([ababe5e](https://github.com/rvben/teams-cli/commit/ababe5e46f24a52d86007de5737cd7601a1ae0c3))
|
|
19
|
+
- **ci**: install pinned Rust components ([f5ecdcc](https://github.com/rvben/teams-cli/commit/f5ecdccfb4ca697ff23cc828393e519a788cbea8))
|
|
20
|
+
|
|
21
|
+
## [0.1.2] - 2026-08-24
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Bundled the maintained multitenant teams-cli Entra public-client registration for zero-ID onboarding.
|
|
26
|
+
- Added explicit `--channel-history` consent and machine-readable authentication capability metadata.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Normal login and token refresh now use only the user-consentable delegated permission baseline.
|
|
31
|
+
- Diagnostics now distinguish successful Microsoft identity access from an unlicensed or unprovisioned Teams tenant.
|
|
32
|
+
|
|
8
33
|
## [0.1.1] - 2026-08-24
|
|
9
34
|
|
|
10
35
|
### Fixed
|
|
@@ -21,6 +46,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
46
|
- Commands for identity, joined teams, channels, chats, and reading or sending messages through Microsoft Graph.
|
|
22
47
|
- Guided onboarding, diagnostics, completions, bounded collections, and deterministic TUI snapshots.
|
|
23
48
|
- Distribution through crates.io, PyPI binary wheels, and signed GitHub release artifacts.
|
|
24
|
-
|
|
25
|
-
[0.1.1]: https://github.com/rvben/teams-cli/compare/v0.1.0...v0.1.1
|
|
26
|
-
[0.1.0]: https://github.com/rvben/teams-cli/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "teams-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.3"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.90"
|
|
6
6
|
description = "A Microsoft Teams CLI and TUI designed equally for humans and agents"
|
|
@@ -31,6 +31,7 @@ serde = { version = "1", features = ["derive"] }
|
|
|
31
31
|
serde_json = "1"
|
|
32
32
|
sha2 = "0.10"
|
|
33
33
|
thiserror = "2"
|
|
34
|
+
tempfile = "3"
|
|
34
35
|
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "io-util", "time"] }
|
|
35
36
|
toml = "1.1"
|
|
36
37
|
url = "2"
|
|
@@ -39,5 +40,4 @@ url = "2"
|
|
|
39
40
|
assert_cmd = "2"
|
|
40
41
|
jsonschema = "0.49.8"
|
|
41
42
|
predicates = "3"
|
|
42
|
-
tempfile = "3"
|
|
43
43
|
wiremock = "0.6"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.PHONY: build test lint fmt check release install clean
|
|
1
|
+
.PHONY: build test lint fmt check release install clean release-patch release-minor release-major
|
|
2
2
|
|
|
3
3
|
build:
|
|
4
4
|
cargo build --locked
|
|
@@ -23,3 +23,12 @@ install: check release
|
|
|
23
23
|
|
|
24
24
|
clean:
|
|
25
25
|
cargo clean
|
|
26
|
+
|
|
27
|
+
release-patch:
|
|
28
|
+
vership bump patch
|
|
29
|
+
|
|
30
|
+
release-minor:
|
|
31
|
+
vership bump minor
|
|
32
|
+
|
|
33
|
+
release-major:
|
|
34
|
+
vership bump major
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teams-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
License-File: LICENSE
|
|
@@ -57,28 +57,44 @@ Run `teams` in a terminal on first use, or start explicitly:
|
|
|
57
57
|
teams init
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
`teams-cli` includes a maintained multitenant Microsoft Entra public-client registration, so normal setup does not require creating an app or copying a client ID:
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
```console
|
|
63
|
+
teams init
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Browser PKCE is the human-friendly default. The browser talks directly to Microsoft, and the CLI never receives or stores your password. Normal sign-in requests these delegated scopes:
|
|
65
67
|
|
|
66
68
|
```text
|
|
67
69
|
openid profile offline_access User.Read Team.ReadBasic.All
|
|
68
70
|
Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
|
|
69
|
-
ChannelMessage.Send
|
|
71
|
+
ChannelMessage.Send
|
|
70
72
|
```
|
|
71
73
|
|
|
72
|
-
Reading channel
|
|
74
|
+
Reading channel history is intentionally separate because `ChannelMessage.Read.All` requires administrator consent. Request it only when needed:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
teams auth login --channel-history
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The CLI surfaces missing consent as a stable `permission_denied` error instead of silently reaching for undocumented Teams APIs. Tenant policy can still require an administrator to approve otherwise user-consentable permissions.
|
|
73
81
|
|
|
74
82
|
Headless setup is explicit and never prompts:
|
|
75
83
|
|
|
76
84
|
```console
|
|
77
|
-
teams init --
|
|
85
|
+
teams init --no-login
|
|
78
86
|
teams auth login --device-code
|
|
79
87
|
teams whoami --output json
|
|
80
88
|
```
|
|
81
89
|
|
|
90
|
+
Organizations that prefer their own Entra registration can override the bundled application:
|
|
91
|
+
|
|
92
|
+
```console
|
|
93
|
+
teams init --client-id "$TEAMS_CLIENT_ID" --tenant organizations --no-login
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The custom app must be a public client with `http://localhost` registered for browser login, public-client flows enabled for device-code login, and the baseline delegated permissions listed above.
|
|
97
|
+
|
|
82
98
|
For short-lived automation, `TEAMS_ACCESS_TOKEN` overrides the stored credential. Refresh credentials are never written to the config file.
|
|
83
99
|
|
|
84
100
|
## Everyday commands
|
|
@@ -88,16 +104,30 @@ teams list
|
|
|
88
104
|
teams channels list TEAM_ID
|
|
89
105
|
teams chats list --limit 25
|
|
90
106
|
teams messages list --chat CHAT_ID
|
|
107
|
+
teams auth login --channel-history # once, with administrator approval
|
|
108
|
+
teams messages list --team TEAM_ID --channel CHANNEL_ID
|
|
91
109
|
teams messages send --chat CHAT_ID --body "On it."
|
|
92
110
|
printf 'Status update' | teams messages send --chat CHAT_ID --body -
|
|
111
|
+
teams config show
|
|
93
112
|
teams doctor
|
|
94
113
|
```
|
|
95
114
|
|
|
96
115
|
Collection commands expose `--limit` and `--fields`; Graph-native collections use `--cursor`, while joined teams and channels use `--offset` because those endpoints do not accept `$top`. With a terminal they render concise text; when stdout is piped they emit JSON envelopes with continuation metadata and `truncated`.
|
|
97
116
|
|
|
117
|
+
Profiles can block every remote write while keeping reads, sign-in, diagnostics, and the TUI available:
|
|
118
|
+
|
|
119
|
+
```console
|
|
120
|
+
teams init --no-login --read-only
|
|
121
|
+
# or for one invocation/environment
|
|
122
|
+
TEAMS_READ_ONLY=true teams messages send --chat CHAT_ID --body "Blocked"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Like the other tools in the suite, the config file is selected with `--profile`, overridden by `TEAMS_*` environment variables, and inspectable without exposing credentials through `teams config show` and `teams config path`.
|
|
126
|
+
|
|
98
127
|
## Boundaries
|
|
99
128
|
|
|
100
129
|
- Microsoft 365 work or school accounts only. The relevant Teams Graph endpoints do not support personal Microsoft accounts.
|
|
130
|
+
- A work account can authenticate successfully before Microsoft Teams is licensed or provisioned; `teams doctor` distinguishes identity access from Teams availability.
|
|
101
131
|
- Calling and screen sharing are not available as a delegated user CLI; Graph calling is built around bot/application scenarios.
|
|
102
132
|
- No extracted desktop tokens, browser interception, or private Teams protocols.
|
|
103
133
|
- Tenant policy and admin consent can restrict delegated capabilities.
|
|
@@ -106,3 +136,8 @@ Collection commands expose `--limit` and `--fields`; Graph-native collections us
|
|
|
106
136
|
|
|
107
137
|
`teams schema` is offline, unauthenticated, and follows [CLI Spec v0.3](https://clispec.dev/): flat invocable command paths, declared effects and cardinality, bounded collections, stable error kinds and exit codes, stdout/stderr separation, and safe noninteractive behavior.
|
|
108
138
|
|
|
139
|
+
## Releasing
|
|
140
|
+
|
|
141
|
+
Vership owns versioning, changelog generation, release commits, and tags. See
|
|
142
|
+
[the release runbook](docs/releases.md) for the verified workflow and recovery policy.
|
|
143
|
+
|
|
@@ -29,12 +29,13 @@ A human-first and agent-first Microsoft Teams terminal client: a polished TUI an
|
|
|
29
29
|
- Installed from PyPI, preferably with `pipx install teams-cli`, despite the implementation being Rust.
|
|
30
30
|
- Invoked as `teams` alongside sibling tools such as `jira` and `confluence`.
|
|
31
31
|
- Used interactively in a local terminal, non-interactively in shell pipelines, and by AI agents.
|
|
32
|
-
- Authenticates a Microsoft 365 work or school account through
|
|
32
|
+
- Authenticates a Microsoft 365 work or school account through the maintained teams-cli Entra public-client application, with an explicit override for customer-owned registrations.
|
|
33
33
|
- Opens the native or web Teams client for experiences that Microsoft Graph does not expose, such as normal user calls.
|
|
34
34
|
|
|
35
35
|
## Capabilities and Constraints
|
|
36
36
|
|
|
37
37
|
- Browser-based delegated OAuth with PKCE is the preferred login; device-code flow is an explicit headless fallback.
|
|
38
|
+
- Everyday commands use a user-consentable baseline; administrator-consented channel history is a deliberate login opt-in.
|
|
38
39
|
- Refresh credentials must use OS-protected storage and must never be written as plaintext tokens.
|
|
39
40
|
- Initial product surface covers onboarding, authentication diagnostics, identity, teams, channels, chats, messages, and the TUI foundation.
|
|
40
41
|
- Commands offer human text and structured JSON output, with clean stdout/stderr separation.
|
|
@@ -41,28 +41,44 @@ Run `teams` in a terminal on first use, or start explicitly:
|
|
|
41
41
|
teams init
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
`teams-cli` includes a maintained multitenant Microsoft Entra public-client registration, so normal setup does not require creating an app or copying a client ID:
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
```console
|
|
47
|
+
teams init
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Browser PKCE is the human-friendly default. The browser talks directly to Microsoft, and the CLI never receives or stores your password. Normal sign-in requests these delegated scopes:
|
|
49
51
|
|
|
50
52
|
```text
|
|
51
53
|
openid profile offline_access User.Read Team.ReadBasic.All
|
|
52
54
|
Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
|
|
53
|
-
ChannelMessage.Send
|
|
55
|
+
ChannelMessage.Send
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
Reading channel
|
|
58
|
+
Reading channel history is intentionally separate because `ChannelMessage.Read.All` requires administrator consent. Request it only when needed:
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
teams auth login --channel-history
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The CLI surfaces missing consent as a stable `permission_denied` error instead of silently reaching for undocumented Teams APIs. Tenant policy can still require an administrator to approve otherwise user-consentable permissions.
|
|
57
65
|
|
|
58
66
|
Headless setup is explicit and never prompts:
|
|
59
67
|
|
|
60
68
|
```console
|
|
61
|
-
teams init --
|
|
69
|
+
teams init --no-login
|
|
62
70
|
teams auth login --device-code
|
|
63
71
|
teams whoami --output json
|
|
64
72
|
```
|
|
65
73
|
|
|
74
|
+
Organizations that prefer their own Entra registration can override the bundled application:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
teams init --client-id "$TEAMS_CLIENT_ID" --tenant organizations --no-login
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The custom app must be a public client with `http://localhost` registered for browser login, public-client flows enabled for device-code login, and the baseline delegated permissions listed above.
|
|
81
|
+
|
|
66
82
|
For short-lived automation, `TEAMS_ACCESS_TOKEN` overrides the stored credential. Refresh credentials are never written to the config file.
|
|
67
83
|
|
|
68
84
|
## Everyday commands
|
|
@@ -72,16 +88,30 @@ teams list
|
|
|
72
88
|
teams channels list TEAM_ID
|
|
73
89
|
teams chats list --limit 25
|
|
74
90
|
teams messages list --chat CHAT_ID
|
|
91
|
+
teams auth login --channel-history # once, with administrator approval
|
|
92
|
+
teams messages list --team TEAM_ID --channel CHANNEL_ID
|
|
75
93
|
teams messages send --chat CHAT_ID --body "On it."
|
|
76
94
|
printf 'Status update' | teams messages send --chat CHAT_ID --body -
|
|
95
|
+
teams config show
|
|
77
96
|
teams doctor
|
|
78
97
|
```
|
|
79
98
|
|
|
80
99
|
Collection commands expose `--limit` and `--fields`; Graph-native collections use `--cursor`, while joined teams and channels use `--offset` because those endpoints do not accept `$top`. With a terminal they render concise text; when stdout is piped they emit JSON envelopes with continuation metadata and `truncated`.
|
|
81
100
|
|
|
101
|
+
Profiles can block every remote write while keeping reads, sign-in, diagnostics, and the TUI available:
|
|
102
|
+
|
|
103
|
+
```console
|
|
104
|
+
teams init --no-login --read-only
|
|
105
|
+
# or for one invocation/environment
|
|
106
|
+
TEAMS_READ_ONLY=true teams messages send --chat CHAT_ID --body "Blocked"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Like the other tools in the suite, the config file is selected with `--profile`, overridden by `TEAMS_*` environment variables, and inspectable without exposing credentials through `teams config show` and `teams config path`.
|
|
110
|
+
|
|
82
111
|
## Boundaries
|
|
83
112
|
|
|
84
113
|
- Microsoft 365 work or school accounts only. The relevant Teams Graph endpoints do not support personal Microsoft accounts.
|
|
114
|
+
- A work account can authenticate successfully before Microsoft Teams is licensed or provisioned; `teams doctor` distinguishes identity access from Teams availability.
|
|
85
115
|
- Calling and screen sharing are not available as a delegated user CLI; Graph calling is built around bot/application scenarios.
|
|
86
116
|
- No extracted desktop tokens, browser interception, or private Teams protocols.
|
|
87
117
|
- Tenant policy and admin consent can restrict delegated capabilities.
|
|
@@ -89,3 +119,8 @@ Collection commands expose `--limit` and `--fields`; Graph-native collections us
|
|
|
89
119
|
## CLI Spec
|
|
90
120
|
|
|
91
121
|
`teams schema` is offline, unauthenticated, and follows [CLI Spec v0.3](https://clispec.dev/): flat invocable command paths, declared effects and cardinality, bounded collections, stable error kinds and exit codes, stdout/stderr separation, and safe noninteractive behavior.
|
|
122
|
+
|
|
123
|
+
## Releasing
|
|
124
|
+
|
|
125
|
+
Vership owns versioning, changelog generation, release commits, and tags. See
|
|
126
|
+
[the release runbook](docs/releases.md) for the verified workflow and recovery policy.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
Vership is the release control plane for `teams-cli`. GitHub Actions is the
|
|
4
|
+
execution plane for cross-platform builds, publication, provenance, and
|
|
5
|
+
downstream packaging.
|
|
6
|
+
|
|
7
|
+
## Create a release
|
|
8
|
+
|
|
9
|
+
Start from a clean, up-to-date `main` branch and run:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
vership preflight
|
|
13
|
+
vership bump patch # or minor/major
|
|
14
|
+
tarry cmd --timeout 20m -- vership verify
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Vership runs `make check`, updates the package version and changelog, creates
|
|
18
|
+
the Conventional Commit release commit and tag, and pushes both. The tag starts
|
|
19
|
+
the release workflow. Use `vership release` only when the on-disk version was
|
|
20
|
+
intentionally set in advance.
|
|
21
|
+
|
|
22
|
+
Cargo and Python package versions are kept synchronized as one release unit.
|
|
23
|
+
|
|
24
|
+
## Failure policy
|
|
25
|
+
|
|
26
|
+
If a release fails before any release, artifact, package, checksum, or
|
|
27
|
+
attestation becomes public, delete and recreate the brief tag and retry the
|
|
28
|
+
same version. Once anything was published, preserve the tag and issue a patch
|
|
29
|
+
release for release-content corrections. Registry and downstream-packaging
|
|
30
|
+
jobs are separate recovery domains; retry only the failed job when possible.
|
|
31
|
+
|
|
32
|
+
## Dry runs
|
|
33
|
+
|
|
34
|
+
The Release workflow can be dispatched from `main` with `dry_run` enabled. It
|
|
35
|
+
uses the package version unless an explicit `version` is supplied, builds the
|
|
36
|
+
real artifacts, and exercises registry validation without publishing packages,
|
|
37
|
+
creating a GitHub release, generating attestations, or updating downstream
|
|
38
|
+
packaging.
|