110 Commits

Author SHA1 Message Date
googleworkspace-bot 0ba49d1584 chore: release versions (#199) v0.6.0 2026-03-05 13:03:38 -08:00
Justin Poehnelt d4080512b1 style: cargo fmt 2026-03-05 13:54:31 -07:00
Justin Poehnelt c532ea6ad8 chore: use labels (#203) 2026-03-05 12:49:44 -08:00
zerone0x b38b760d79 feat: add Application Default Credentials (ADC) support (#125)
* feat: add Application Default Credentials (ADC) support (#103)

Extends the credential chain in get_token() to include ADC as a 4th source:
  1. GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE env var
  2. Encrypted credentials (~/.config/gws/credentials.enc)
  3. Plaintext credentials (~/.config/gws/credentials.json)
  4. ADC — GOOGLE_APPLICATION_CREDENTIALS env var, then
     ~/.config/gcloud/application_default_credentials.json

Both authorized_user and service_account ADC formats are detected via the
'type' field and parsed accordingly.  This means users can authenticate with:
  gcloud auth application-default login --client-id-file=client_secret.json

and gws will automatically pick up those credentials.

Closes #103

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(auth): address review feedback on ADC support

- Extract duplicated JSON credential parsing into parse_credential_file()
  helper to reduce duplication between GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE
  and ADC code paths; uses serde_json::from_value to avoid second string parse
- Fix well-known ADC path on macOS: dirs::config_dir() returns
  ~/Library/Application Support on macOS, not ~/.config; use
  dirs::home_dir().join('.config/gcloud/...') instead
- Hard-error when GOOGLE_APPLICATION_CREDENTIALS points to a missing file
  (was: silently fall through to 'No credentials found')
- Add test_load_credentials_adc_env_var_service_account covering service
  account credentials loaded via GOOGLE_APPLICATION_CREDENTIALS
- Remove unnecessary unsafe blocks from env var tests (set_var/remove_var
  are not unsafe functions; thread safety is already handled by serial_test)
- Update changeset to include GOOGLE_WORKSPACE_CLI_TOKEN at top of lookup
  order and clarify ADC fallback behaviour

Addresses review feedback from jpoehnelt on #125.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-05 13:23:13 -07:00
googleworkspace-bot c6095ddb27 chore: release versions (#180) v0.5.0 2026-03-05 09:56:15 -08:00
Hafez 5205467ecb fix(setup): drain stale keypresses between TUI screen transitions (#186)
* fix(setup): drain stale keypresses between TUI screen transitions

The picker's vim-style j/k navigation can leave buffered keypresses in
crossterm's event queue that leak into the next input field, corrupting
pre-populated values like client_id.

* refactor: extract drain logic into helper, add changeset
2026-03-05 09:39:05 -08:00
Justin Poehnelt 62d6ddbfa9 chore: steps to reduce contributor friction (#185)
* chore: steps to reduce contributor friction

* Update CLAUDE.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-05 09:08:50 -08:00
Justin Poehnelt 473dd30500 chore: cargo fmt 2026-03-05 09:17:50 -07:00
Frank 9cf6e0e66d feat(mcp): add compact tool mode to reduce context window usage (#172) 2026-03-05 08:17:19 -08:00
Justin Poehnelt 0a16d0bca3 fix: allow services flag for login and improve default list for consumer accounts (#177)
* feat: add -s/--services flag to auth login for scope picker filtering

* fix: restrict recommended scope template to minimal consumer scopes

* refactor: address PR review comments — dedup parsing, extract helpers, use is_none_or
2026-03-05 08:14:54 -08:00
Mattia Righetti fa04502d14 chore(docs): easier cmd to install from source (#176)
* chore(docs): easier cmd to install from source

* Apply suggestion from @gemini-code-assist[bot]

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-05 08:10:25 -08:00
googleworkspace-bot f6d74b0074 chore: release versions (#147) v0.4.4 2026-03-05 01:14:25 -08:00
Justin Poehnelt e1e08ebfb6 fix: use reverse video for TUI highlight style (closes #139) (#146)
Replace bg(Color::DarkGray) with Modifier::REVERSED in highlight_style
so the selected-row highlight adapts to both light and dark terminal themes.

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-05 01:08:03 -08:00
googleworkspace-bot b9c3698595 chore: release versions (#142) v0.4.3 2026-03-05 00:52:26 -08:00
Justin Poehnelt 2aa6084e59 docs: improve readme especially auth which is complex no matter what (#136)
* docs: improve readme especially auth which is complex no matter what

* docs: add changeset for README overhaul

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-05 00:47:55 -08:00
zerone0x fc6bc9593b fix: exclude Workspace-admin-only scopes from Recommended preset (#119) (#127)
Admin-only scopes (apps.*, cloud-identity.*, ediscovery, directory.readonly,
groups) require Workspace domain-admin access and cannot be granted to personal
@gmail.com accounts — Google returns 400 invalid_scope when they're included.

Changes:
- Add is_workspace_admin_scope() helper (mirrors is_app_only_scope())
  to identify scopes that fail for personal Google accounts
- Exclude these scopes from the template_selects of the 'Recommended' preset
  in run_discovery_scope_picker()
- Exclude them from the resolved scope list when the Recommended template
  is confirmed
- Add 8 unit tests covering the new helper

Workspace admins can still access these scopes via 'Full Access' template
or by selecting them individually in the picker.

Note: this is complementary to PR #108 which filters alertcenter scopes
at the API-discovery level. This PR handles the broader set at the
recommendation layer.

Addresses #119 (Bug 1: admin scopes in Recommended preset)

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-05 00:44:46 -08:00
googleworkspace-bot 29a029aef4 chore: release versions (#140) v0.4.2 2026-03-05 00:44:04 -08:00
Justin Poehnelt 2590768222 style: fmt for linter 2026-03-05 01:42:13 -07:00
Frank d3e90e4931 fix: use ~/.config/gws on all platforms for consistent config path (#134)
Previously used dirs::config_dir() which resolves to different paths per
OS (~/Library/Application Support/gws on macOS, %APPDATA%\gws on Windows),
contradicting the documented ~/.config/gws/ path and causing users to place
config files in the wrong location (ref #119).

Now uses ~/.config/gws/ everywhere with a fallback to the legacy OS-specific
path for existing installs. Also consolidates duplicated dirs::config_dir()
calls in auth.rs and discovery.rs to use the central config_dir() helper.
2026-03-05 01:30:19 -07:00
googleworkspace-bot 6ed836c81c chore: release versions (#132) v0.4.1 2026-03-05 00:10:19 -07:00
googleworkspace-bot 7fa8bda23d chore: release versions (#131) 2026-03-04 23:55:30 -07:00
zerone0x dbda001367 fix: add manual project ID entry to setup project picker (#116) (#123)
When `gcloud projects list` times out (10s limit) for users with many
projects, the picker now includes a '⌨ Enter project ID manually'
option so they can type a known project ID instead of waiting or failing.

- Add '⌨ Enter project ID manually' item to project picker
- Handle the new item by prompting for input and calling set_gcloud_project
- Add EnterProjectId variant to SetupAction (tests)
- Add test_project_select_enter_manually unit test

Fixes #116

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-04 23:54:41 -07:00
Joe Eftekhari 364542b2c5 fix: reject DEL character (0x7F) in input validation (#122)
The reject_control_chars helper rejected bytes 0x00-0x1F but allowed
the DEL character (0x7F), which is also an ASCII control character.
This could allow malformed input from LLM agents to bypass validation.
2026-03-04 23:50:09 -07:00
Frank 263a8e5479 fix: use gcloud.cmd on Windows and show platform-correct config paths (#126)
* fix: use gcloud.cmd on Windows and show platform-correct config paths

On Windows, Google Cloud SDK installs `gcloud.cmd` (not `gcloud.exe`).
Rust's `Command::new("gcloud")` does not search PATHEXT, so all gcloud
invocations failed silently. Add `gcloud_bin()` helper that returns
`gcloud.cmd` on Windows and `gcloud` elsewhere.

Also replace hardcoded `~/.config/gws/` paths in error messages with
the actual platform-resolved path (`%APPDATA%\gws\` on Windows).

* chore: add changeset for Windows gcloud compat fix
2026-03-04 23:48:27 -07:00
Devadath S 75cec1b444 fix: URL template rendering for upload endpoints (#129) 2026-03-04 23:46:14 -07:00
Evan 87e4bb1dc4 feat: add Linux ARM64 build targets (gnu + musl) (#128)
* feat: add Linux ARM64 build targets (gnu + musl)

Add aarch64-unknown-linux-gnu and aarch64-unknown-linux-musl to
cargo-dist targets, enabling prebuilt binaries for ARM64 Linux
users via npm, the shell installer, and GitHub Releases.

* chore: trigger CLA recheck
2026-03-04 23:44:22 -07:00
Andrew Barnes a6994ad068 fix: filter alertcenter scopes from user OAuth login flow (#108)
* fix: filter alertcenter scopes from user OAuth login flow

The `apps.alerts` scope is restricted to service accounts with
domain-wide delegation and fails with `400 invalid_scope` when
used in the standard 3-legged OAuth consent flow. Filter it out
alongside the existing chat.app/chat.bot/keep exclusions.

Fixes #73

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: also filter apps.alerts in is_app_only_scope()

The scope filter exists in two locations: setup.rs (fetch_scopes_for_apis)
and auth_commands.rs (is_app_only_scope). Both need the apps.alerts
exclusion to prevent it from appearing in the interactive scope picker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: retrigger CLA check

* chore: retrigger CI after CLA signing

---------

Co-authored-by: Andrew Barnes <andrew.jaguars@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 23:37:57 -07:00
Justin Poehnelt cdae30ff4a chore: fix changesets 2026-03-04 23:31:56 -07:00
Joe Eftekhari 1ad4f34da6 fix: replace unwrap() calls with proper error handling in MCP server (#109)
* fix: replace unwrap() calls with proper error handling in MCP server

Replace four serde_json::to_string().unwrap() calls in the MCP server
that could panic the process if serialization ever fails. Instead, log
the error to stderr and skip the response gracefully.

Also propagate serialization errors for params/body via the ? operator,
and log a warning when authentication silently falls back to
unauthenticated mode so users are aware of degraded state.

* fix: resolve clippy redundant_closure warnings

Replace `.map(|v| serde_json::to_string(v))` with
`.map(serde_json::to_string)` per clippy's redundant_closure lint.
2026-03-04 23:29:57 -07:00
Justin Poehnelt ed409e3022 fix: harden URL and path construction across helper modules (#102)
* fix: harden URL and path construction across helper modules

Closes #87

- gmail/watch.rs: encode msg_id with encode_path_segment(), use
  .query() for format and history params instead of format!
- modelarmor.rs: validate template with validate_resource_name() in
  handle_sanitize, validate project/location/template_id in
  parse_create_template_args, encode all path segments in
  build_create_template_url
- discovery.rs: validate service/version with validate_api_identifier()
  before use in cache filenames and discovery URLs, encode path segments
- validate.rs: add validate_api_identifier() for safe API name chars
- Add tests for all new validation and encoding paths

* refactor: pass API version as a query parameter for alternative discovery URLs.
2026-03-04 23:29:34 -07:00
andrew-kline a1be14f0c7 fix: drain stdout pipe to prevent project listing deadlock (#106)
The list_gcloud_projects() function piped stdout from `gcloud projects
list` but only read it after the child process exited. When a user has
enough GCP projects that the output exceeds the OS pipe buffer (~64KB),
gcloud blocks on write, the parent blocks waiting for exit, and neither
side makes progress — hitting the 10s timeout.

Spawn a thread to drain stdout concurrently so the pipe buffer never
fills up while the main thread polls for process completion.

Fixes googleworkspace/cli#96
2026-03-04 23:28:56 -07:00
Justin Poehnelt d1825f9385 feat: multi-account support (#85)
* feat: multi-account support with --account flag, per-account credential storage

- Add --account global flag and GOOGLE_WORKSPACE_CLI_ACCOUNT env var
- Per-account encrypted credential files (credentials.<b64-email>.enc)
- Per-account token cache (token_cache.<b64-email>.json)
- accounts.json registry with default account tracking
- New auth subcommands: list, default, per-account logout
- login_hint in OAuth URL for account pre-selection
- Email validation via Google userinfo after OAuth flow
- 12 new unit tests (380 total)

BREAKING CHANGE: Existing users must run 'gws auth login' again.
Credential storage changed from single credentials.enc to per-account files.

* refactor: Improve error handling for file system operations, rename `GWS_ACCOUNT` to `GOOGLE_WORKSPACE_CLI_ACCOUNT`, and refine service account token cache path generation.

* fix: clean up per-account token caches on logout

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-04 23:19:14 -07:00
Justin Poehnelt 805e037c74 docs: Remove logo image from README for reasons
Removed logo image from README and retained title and description.
2026-03-04 23:16:49 -07:00
googleworkspace-bot 5ebaf9cd31 chore: release versions (#91) v0.3.5 2026-03-04 23:05:50 -07:00
Justin Poehnelt 70d0cdddb5 fix: build_url now falls back to method.path when flatPath placeholders do not match parameter names, resolving Slides API issues. (#120) 2026-03-04 23:02:56 -07:00
Justin Poehnelt 1991d536b4 docs: add note about not official product more prominently (#115) 2026-03-04 22:45:22 -07:00
Steve Bazyl 37ab483840 feat: Add nix flake (#94) 2026-03-04 22:21:36 -07:00
Justin Poehnelt 99a6284cb2 chore: fix package name 2026-03-04 20:29:57 -07:00
Miguel 🦈 4bca6935d6 fix: credential masking panic and silent token write errors (#81)
Two bugs fixed:

1. `auth_commands.rs` — `gws auth export` masking used `s[..4]` which
   panics on strings shorter than 4 characters, and `s[s.len().min(4)..]`
   which evaluates to `s[4..]` on long strings — showing the entire
   secret instead of masking it. Replaced with a `mask_secret()` helper
   that safely shows only the first 4 and last 4 characters, or "***"
   for short strings.

2. `token_storage.rs` — `save_to_disk` silently discarded the return
   value of `atomic_write_async` with `let _`, causing the function to
   return `Ok(())` even when the write failed. Token persistence failures
   now properly propagate via `?`.
2026-03-04 20:28:28 -07:00
kai f84ce37007 fix: encode URL template path params in build_url (#84)
* fix: encode URL path template params in build_url

* chore: trigger CLA recheck

* fix: validate +path params and align replacement logic
2026-03-04 20:27:50 -07:00
Hasala Abhilasha eb0347a19e fix: correct author email typo in package.json (#89) 2026-03-04 20:26:06 -07:00
googleworkspace-bot 0d9558d297 chore: release versions (#80) v0.3.4 2026-03-04 18:54:05 -07:00
Vedant Thapa a18d17374b fix: add github.com instead of just github in readme (#79)
* fix: add github.com instead of just github in readme

* Update README.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-04 18:52:32 -07:00
Justin Poehnelt 7044602c38 chore: fix changeset name 2026-03-04 18:47:16 -07:00
Justin Poehnelt 704928bd99 fix: enable APIs individually and surface gcloud errors (#77)
* fix: enable APIs individually and surface gcloud errors

Previously, `gws auth setup` used a single batch `gcloud services enable`
call for all 22 Workspace APIs. If any one API failed, the entire batch
was marked as failed. Additionally, stderr was piped to /dev/null, so
users never saw why APIs failed to enable.

Changes:
- Enable each API individually so one failure doesn't block the rest
- Capture stderr from gcloud and include error messages in output
- Show failure details with ⚠ warnings in the wizard status
- Include structured error objects in JSON output (apis_failed array)

Fixes #75

* address review: parallelize API enablement with tokio, remove clone

- Use tokio::process::Command + futures_util::stream::buffer_unordered(5)
  for parallel, non-blocking API enablement
- Remove unnecessary failed_apis.clone() by moving into ctx.failed first
- Add changeset

* test: add coverage for enable_apis and JSON output structure

- test_enable_apis_all_already_enabled: empty input returns empty results
- test_enable_apis_with_invalid_project: verifies errors are captured (not swallowed)
- test_failed_apis_json_structure: validates {api, error} JSON shape
- test_failed_apis_json_empty: empty failures produce empty array

* Update src/setup.rs

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-04 18:44:59 -07:00
googleworkspace-bot 6092bf90a0 chore: release versions (#72) v0.3.3 2026-03-04 15:30:51 -08:00
Justin Poehnelt 92e66a308f feat: add gws version bare subcommand (#71)
* feat: add `gws version` bare subcommand

* refactor: extract help and version flag checks into dedicated functions and add test coverage guidance to AGENTS.md

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-04 15:29:02 -08:00
googleworkspace-bot 680b60cecc chore: release versions (#70) v0.3.2 2026-03-04 15:06:12 -08:00
Justin Poehnelt 8fadbd6901 feat: smarter truncation of method/resource descriptions (#68)
* feat: smarter truncation of method/resource descriptions

- Add shared truncate_description() in src/text.rs with configurable
  limits and optional markdown link stripping
- Named constants: CLI_DESCRIPTION_LIMIT (200), FRONTMATTER_DESCRIPTION_LIMIT (120),
  SKILL_BODY_DESCRIPTION_LIMIT (500)
- CLI help and frontmatter strip markdown links to save space
- Skill body preserves URLs so agents can follow references
- Truncation prefers sentence boundaries, falls back to word boundaries
- Reads full descriptions from discovery doc for skill body text

Fixes #64

* fix: address PR review comments and improve coverage

- Fix sentence boundary detection at exact end of prefix (HIGH)
- Use extend() for link text emission (MEDIUM)
- Idiomatic find_char_from with position/map (MEDIUM)
- Idiomatic rfind_char_boundary with rposition (MEDIUM)
- Add 18 new tests for generate_skills.rs coverage:
  truncate_desc, lookup_method_description, capitalize_first,
  product_name_from_title
- Add sentence_boundary_at_exact_limit and zero_max_chars tests

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-04 15:03:59 -08:00
googleworkspace-bot bcc6c8d24e chore: release versions (#62) v0.3.1 2026-03-04 15:24:51 -07:00