283 Commits

Author SHA1 Message Date
Simon
a3768d0e82 feat(sheets): add --range flag to +append for targeting specific tabs (#641)
Previously +append always used hardcoded "A1" range, limiting appends
to the first sheet. Add optional --range flag (default: "A1") to allow
appending to any sheet tab using A1 notation.
2026-03-31 12:51:15 -06:00
googleworkspace-bot
705fb0ecac chore: release versions (#655) v0.22.5 2026-03-31 12:32:18 -06:00
Justin Poehnelt
b307856f6a refactor: drop serde_yaml by migrating to toml (#657)
* refactor: migrate skill registry from yaml to toml to drop unmaintained serde_yaml dependency

* chore: add changeset for yaml to toml migration

* fix(tests): resolve TOML test suite formatting
2026-03-31 12:22:48 -06:00
Justin Poehnelt
503315bc42 docs: update installation instructions to prioritize github releases (#656)
* docs: update installation instructions to prioritize github releases

* ci: add explicit verification instructions to github release notes

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 12:11:46 -06:00
Justin Poehnelt
ecddf2ec86 ci: add cargo-deny for license, advisory, and source auditing (#653)
* ci: add cargo-deny configuration for supply chain auditing

* Update deny.toml

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

* Update deny.toml

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

* Update deny.toml

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

* Update deny.toml

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

* Update deny.toml

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

* ci: add cargo-deny to workflow

Integrates cargo-deny into the main CI pipeline using the official
EmbarkStudios action. This will automatically block PRs that introduce
banned licenses, insecure crates, or unallowed registries.

* ci: explicitly deny unmaintained, yanked, and unlicensed crates per review

* ci: remove deprecated cargo-deny keys and restore valid config

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-31 12:09:56 -06:00
Justin Poehnelt
6ccbb42650 fix: auto-install binary on run if missing (#654)
* fix: auto-install binary on run if missing

pnpm skips postinstall when the package is already cached/installed.
This commit ensures run.js will auto-trigger install.js if the
binary is missing, fixing the 'gws binary not found' error.

* chore: add changeset for pnpm binary fix

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 11:47:43 -06:00
Justin Poehnelt
5d24ac225b ci: add cargo-audit workflow for dependency vulnerability scanning (#649)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 11:42:06 -06:00
Justin Poehnelt
158f93afd7 fix: verify SHA256 checksum in npm postinstall script (#650)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 11:41:25 -06:00
Justin Poehnelt
b422e5d22f ci: pin cross-rs to v0.2.5 in release workflow (#651)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 11:40:47 -06:00
googleworkspace-bot
f4ee658360 chore: release versions (#647) v0.22.4 2026-03-31 11:20:03 -06:00
Justin Poehnelt
86c08cfc0d fix: remove cargo-dist, use native fetch for npm installer (#646)
* chore: remove cargo-dist, use native fetch for npm installer

* fix: harden npm scripts — spawnSync, signal handling, binary-exists check

* fix: address PR review comments

- Add null body guard for fetch response
- Fix PowerShell Expand-Archive path quoting vulnerability
- Sanitize error output to prevent ANSI escape injection
- Add proxy support limitation note
- Fix upgrade bug: use .version marker so npm update downloads new binary
- Downgrade changeset from minor to patch (chore, not feature)
- Update AGENTS.md: remove stale cargo-dist reference from labels

* fix: use flat archives for consistent tar/zip extraction

Both tar.gz and zip archives now contain files at root (no nested
directory). Removes --strip-components 1 from install.js since it is
no longer needed. This makes extraction consistent across platforms.

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-31 11:17:57 -06:00
googleworkspace-bot
c7c6646d7a chore: release versions (#629) v0.22.3 2026-03-26 12:45:06 -06:00
googleworkspace-bot
ec7f56bc39 chore: regenerate skills from Discovery API (#630)
Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
2026-03-26 12:40:24 -06:00
Justin Poehnelt
80bd150f76 feat(auth): use strict OS keychain integration on macOS and Windows (#631)
* fix(auth): implement platform-aware keyring storage

* chore: regenerate skills [skip ci]

* fix(auth): address PR comments on error handling and TOCTOU vulnerabilities

* fix(test): isolate config directory to prevent race conditions during cargo test

* refactor(auth): deduplicate key generation logic in credential store

* fix(test): execute credential tests serially to prevent environment pollution

* fix(auth): sanitize os keyring error strings and handle missing file fallbacks

This commit removes the inadvertently included skills, uses output::sanitize_for_terminal to protect the user from escape sequence injection via keyring errors, and ensures removal of the fallback file properly alerts the user when an io error occurs.

* chore: regenerate skills [skip ci]

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-26 12:40:07 -06:00
jpoehnelt-bot
674d53a65a fix(ci): add setup-uv step to Lint Skills job
The Lint Skills job uses uvx to run agentskills validate, but uv
is not pre-installed on GitHub Actions runners. Add astral-sh/setup-uv
action and a skills path filter to the changes detection.
2026-03-26 09:59:40 -06:00
jpoehnelt-bot
c7c42f6d07 fix: register script service and fix test path validation 2026-03-26 09:59:14 -06:00
googleworkspace-bot
a63b070a51 chore: release versions (#628) v0.22.2 2026-03-26 09:56:34 -06:00
femto
a52d297cdf feat: add HTTP proxy support via environment variables (#622)
* feat: add HTTP proxy support via environment variables

When http_proxy/https_proxy/all_proxy environment variables are set,
use reqwest (which natively supports proxy) for token refresh instead
of yup-oauth2's hyper-based client (which doesn't support proxy).

This enables gws to work in environments that require HTTP proxy to
access Google APIs (e.g., users in China).

Changes:
- Cargo.toml: Enable reqwest's default features including proxy support
- src/auth.rs: Add proxy-aware token refresh using reqwest as fallback

Fixes #422

* feat: add proxy support for auth login flow

When proxy env vars are set, use a custom OAuth flow with reqwest
for token exchange instead of yup-oauth2's hyper-based client.

Changes to auth_commands.rs:
- Add login_with_proxy_support() for proxy-aware OAuth login
- Add exchange_code_with_reqwest() for token exchange via reqwest
- Detect proxy env vars and choose appropriate flow

* fix: address proxy auth review feedback

* fix: reuse shared reqwest client for auth flows
2026-03-25 17:31:22 +00:00
googleworkspace-bot
45ca45bfd4 chore: release versions (#624) v0.22.1 2026-03-25 11:31:07 -06:00
Yannik Tausch
b45cdf862a ci: add skills-ref lint step for SKILL.md validation (#626) 2026-03-25 09:44:54 -06:00
googleworkspace-bot
6a45832ea5 chore: regenerate skills from Discovery API (#621)
Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
2026-03-24 23:07:45 -06:00
Justin Poehnelt
19ddc25935 fix: move data files into CLI crate for crates.io publishing (#620)
* fix: move registry/ and templates/ into CLI crate for cargo publish

* chore: regenerate skills [skip ci]

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 16:35:41 -06:00
googleworkspace-bot
9198386a3d chore: release versions (#619) v0.22.0 2026-03-24 16:17:11 -06:00
Malo Bourgon
0850c48b68 feat(gmail): add --draft flag to +send, +reply, +reply-all, +forward (#571)
When --draft is set, calls users.drafts.create instead of
users.messages.send. Message construction is identical; only the
API method and metadata wrapper change. Threaded drafts (replies
and forwards) preserve threadId in the draft metadata.
2026-03-24 16:01:55 -06:00
googleworkspace-bot
2aec0ba5a3 chore: release versions (#618)
* chore: release versions

* chore: regenerate skills [skip ci]

---------

Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 15:51:18 -06:00
Justin Poehnelt
c4448b98a6 chore: add changeset for crates.io publishing (#617)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-24 15:32:19 -06:00
googleworkspace-bot
186e88caa0 chore: release versions (#616) 2026-03-24 15:01:28 -06:00
Justin Poehnelt
ea0849a657 fix: version sync workspace (#615) 2026-03-24 14:53:39 -06:00
googleworkspace-bot
eacd436694 chore: release versions (#614) v0.21.0 2026-03-24 14:44:26 -06:00
Justin Poehnelt
029e5def2b feat: extract google-workspace library crate (cargo workspace) (#613)
* feat: extract google-workspace library crate and restructure as cargo workspace

- Create crates/google-workspace/ with public modules: discovery, error, services, validate, client
- Move CLI binary to crates/cli/ (package: google-workspace-cli, binary: gws)
- Root Cargo.toml is now workspace-only
- Binary modules use thin re-exports from library (zero behavioral changes)
- Discovery fetch_discovery_document accepts cache_dir parameter for library consumers
- Break output.rs / error.rs circular dep by moving char detection to library validate module
- Update CI workflows for workspace (--workspace flags, path filters)
- Update dist-workspace.toml and policy.yml for new crate locations

Closes #386

* refactor: rename crates/cli to crates/google-workspace-cli

* chore: sync CLI version to 0.20.1 and regenerate skills

* chore: update labeler paths for workspace crate layout

* fix: update flake.nix to read version from CLI crate Cargo.toml

* fix: use tokio::fs for non-blocking I/O in library discovery cache

* docs: update AGENTS.md source layout for workspace structure

* docs: add crate-level READMEs for crates.io

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-24 14:40:03 -06:00
googleworkspace-bot
3ac16ee595 chore: release versions (#606)
* chore: release versions

* chore: regenerate skills [skip ci]

---------

Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 13:28:51 -06:00
Justin Poehnelt
2ddb46e39f test(gmail): add regression tests for RFC 2822 display name quoting (#610)
The original bug from #513 (display names with commas/parens causing
'Invalid To header') was already fixed by the mail-builder migration
in #482. This adds explicit regression tests to prevent regressions.

Closes #513

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-24 13:25:54 -06:00
Justin Poehnelt
b8fd3d966f fix(client): add 10s connect timeout to prevent initial hangs (#608)
* fix(client): add 10s connect timeout to prevent initial hangs

* fix(client): retry on transient connect/timeout errors

* chore: regenerate skills [skip ci]

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 12:49:23 -06:00
googleworkspace-bot
75a7121713 chore: sync skills with Discovery API (#607)
* chore: regenerate skills from Discovery API

* chore: regenerate skills [skip ci]

---------

Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 12:40:27 -06:00
Yannik Tausch
2bfcca97ca feat: move version from top-level SKILL.md frontmatter to metadata and track CLI version (#601) 2026-03-24 11:59:55 -06:00
Justin Poehnelt
203acc7bcf fix(ci): skip publish-skills on fork PRs where secrets are unavailable (#605)
Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
v0.20.0
2026-03-24 11:59:37 -06:00
googleworkspace-bot
3e1e0e11f3 chore: release versions (#603)
* chore: release versions

* chore: regenerate skills [skip ci]

---------

Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 11:50:23 -06:00
Justin Poehnelt
4663021b4a fix(ci): skip publish-skills on fork PRs (#604)
* fix(ci): skip publish-skills on fork PRs where secrets are unavailable

* chore: regenerate skills [skip ci]

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-24 11:50:06 -06:00
Malo Bourgon
e782dd70b5 feat(gmail): forward original attachments and preserve inline images (#589)
Include original message attachments on +forward by default, matching
Gmail web behavior. Add --no-original-attachments flag to opt out
(skips file attachments but preserves inline images in HTML mode).

Preserve cid: inline images in HTML mode for both +forward and
+reply/+reply-all by building the correct multipart/related MIME
structure via mail-builder's MimePart API. Gmail's API rewrites
Content-Disposition: inline to attachment in multipart/mixed, so
explicit multipart/related is required.

In plain-text mode, inline images are not included for both forward
and reply, matching Gmail web behavior.

Key implementation details:
- Single-pass MIME payload walker replaces separate text/html extractors
- OriginalPart metadata type with lazy attachment data fetching
- Part classification uses Content-Disposition to distinguish regular
  attachments from inline images (some clients set Content-ID on both)
- Content-ID and content_type sanitized against CRLF header injection
- Size preflight before downloading original attachments
- Remote filename sanitization (not rejection) for sender-controlled names
- Walker does not recurse into hydratable parts (e.g., message/rfc822)
2026-03-24 11:32:22 -06:00
Justin Poehnelt
477f5d90db docs: add helper command guidelines and anti-patterns (#598)
* docs: add helper command guidelines and anti-patterns

* Apply suggestions from code review

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

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
v0.19.0
2026-03-23 15:27:03 -06:00
googleworkspace-bot
834fa7d25a chore: release versions (#590)
* chore: release versions

* chore: regenerate skills [skip ci]

---------

Co-authored-by: googleworkspace-bot <googleworkspace-bot@users.noreply.github.com>
2026-03-23 14:49:28 -06:00
github-actions[bot]
e82c02c5a2 style: cargo fmt 2026-03-23 20:38:28 +00:00
JoeVenner
8a749c2c11 feat: add --dry-run support to events helper commands (#457)
Add dry-run mode to gws events +renew and gws events +subscribe commands.
When --dry-run is specified, the commands print what actions would be
taken without making any API calls. This allows agents to simulate
requests and learn without reaching the server.
2026-03-23 14:38:17 -06:00
Adem Akdoğan
f157208449 fix: use block-style YAML sequences in SKILL.md frontmatter (#580)
Replace flow sequences (bins: ["gws"], skills: [...]) with block-style
sequences in all generated SKILL.md frontmatter templates.

Flow sequences are valid YAML but rejected by strictyaml, which the
Agent Skills reference implementation (agentskills validate) uses to
parse frontmatter. This caused all 93 generated skills to fail
validation.

Also adds unit tests verifying that service, shared, persona, and
recipe skill templates produce block-style sequences only.

Fixes #521
2026-03-23 14:31:57 -06:00
Justin Poehnelt
a078945f2a refactor: replace manual arg parsing in auth commands with clap (#594)
* refactor: replace manual arg parsing in auth commands with clap

- Add ScopeMode enum (Default, Readonly, Full, Custom) for type-safe
  scope selection
- Build auth_command() with clap subcommands: login, setup, status,
  export, logout
- Replace manual --help check and match dispatch in handle_auth_command
  with clap subcommand routing
- Replace two-pass manual parsing in handle_login (services extraction)
  and resolve_scopes (scopes/readonly/full flags) with single clap parse
- Add --unmasked flag to export subcommand via clap
- Preserve run_login(&[]) public API for setup.rs compatibility
- Update all 63 tests to use ScopeMode-based API

* refactor: extract parse_login_args helper to deduplicate scope/services parsing

* fix: make --readonly, --full, --scopes mutually exclusive via clap conflicts

* fix: filter empty strings from custom scopes parsing

* refactor: extract build_login_subcommand to eliminate fragile .expect() lookup

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-23 14:25:42 -06:00
Justin Poehnelt
d341de29a2 fix(setup): handle --help/-h before launching setup wizard (#592)
* fix(setup): handle --help/-h before launching setup wizard

Check for --help/-h at the top of run_setup() before any work that
requires gcloud. Previously, running 'gws auth setup --help' would
launch the full setup flow.

Fixes #280

* refactor: extract SETUP_USAGE into const per review

* refactor: replace manual setup arg parsing with clap

Use clap::Command for gws auth setup argument parsing instead of manual
while-loop parser. This gives us:
- Automatic --help/-h handling (no manual check needed)
- Proper error messages for unknown flags
- Consistent with other clap usage in the codebase
- Help text stays in sync with actual arguments

* fix: distinguish --help (clean exit) from invalid flags (error)

Change parse_setup_args return type from Result<SetupOptions, ()> to
Result<Option<SetupOptions>, GwsError> so that:
- Ok(Some(opts)) = successful parse
- Ok(None) = --help/--version displayed (clean exit, code 0)
- Err(GwsError) = invalid flags (error exit, non-zero code)

* fix: propagate e.print() IO errors instead of silently ignoring

---------

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-23 13:08:28 -06:00
Justin Poehnelt
d6794019f8 fix(auth): prevent mask_secret panic on multi-byte UTF-8 secrets (#591)
Use char-based indexing instead of byte-offset slicing in mask_secret()
to prevent panics when secrets contain multi-byte UTF-8 characters
(accented letters, emoji, CJK, etc.).

Co-authored-by: jpoehnelt-bot <jpoehnelt-bot@users.noreply.github.com>
2026-03-23 12:00:16 -06:00
Justin Poehnelt
01fb4f297c chore: fix package name in changeset 2026-03-23 11:25:35 -06:00
Sidharth Rajmohan
b4d5e26424 fix(auth): propagate error when token directory cannot be created (#542)
* fix(auth): propagate errors when token directory creation/permissions fail

Previously, failures to create the token directory or set its permissions
were silently ignored using 'let _ = ...'. This could lead to confusing
errors later or security issues if permissions were left insecure.

Now properly propagates errors from:
- tokio::fs::create_dir_all()
- std::fs::set_permissions() (on Unix)

Also sanitizes the path in error messages to prevent terminal escape
sequence injection, aligned with codebase security practices.

* fix(auth): use spawn_blocking for set_permissions to avoid blocking async runtime

Following the reviewer suggestion, std::fs::set_permissions is now executed
via tokio::task::spawn_blocking to avoid potentially blocking the async
runtime thread, which can cause performance issues or deadlocks under load.

* fix(auth): use tokio::fs::set_permissions instead of spawn_blocking

Simplifies the code by using Tokio's native async set_permissions,
removing the need for manual thread spawning.
2026-03-23 11:25:03 -06:00
googleworkspace-bot
e9970db26f chore: release versions (#553) v0.18.1 2026-03-18 14:56:16 -06:00