crewly 1.20.35 → 1.20.40

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.
Files changed (77) hide show
  1. package/config/skills/_common/lib.sh +6 -0
  2. package/config/skills/agent/core/calendar-create/SKILL.md +10 -0
  3. package/config/skills/agent/core/calendar-create/execute.sh +6 -0
  4. package/config/skills/agent/core/calendar-list/SKILL.md +10 -0
  5. package/config/skills/agent/core/calendar-list/execute.sh +6 -0
  6. package/config/skills/agent/core/docs-read/SKILL.md +10 -0
  7. package/config/skills/agent/core/docs-read/execute.sh +6 -1
  8. package/config/skills/agent/core/docs-write/SKILL.md +10 -0
  9. package/config/skills/agent/core/docs-write/execute.sh +6 -1
  10. package/config/skills/agent/core/drive-read/SKILL.md +10 -0
  11. package/config/skills/agent/core/drive-read/execute.sh +6 -1
  12. package/config/skills/agent/core/drive-search/SKILL.md +10 -0
  13. package/config/skills/agent/core/drive-search/execute.sh +6 -1
  14. package/config/skills/agent/core/drive-upload/SKILL.md +10 -0
  15. package/config/skills/agent/core/drive-upload/execute.sh +6 -1
  16. package/config/skills/agent/core/gmail-read/SKILL.md +10 -0
  17. package/config/skills/agent/core/gmail-read/execute.sh +6 -0
  18. package/config/skills/agent/core/gmail-search/SKILL.md +10 -0
  19. package/config/skills/agent/core/gmail-search/execute.sh +6 -0
  20. package/config/skills/agent/core/gmail-send/SKILL.md +10 -0
  21. package/config/skills/agent/core/gmail-send/execute.sh +6 -0
  22. package/config/skills/agent/core/sheets-read/SKILL.md +10 -0
  23. package/config/skills/agent/core/sheets-read/execute.sh +6 -1
  24. package/config/skills/agent/core/sheets-write/SKILL.md +10 -0
  25. package/config/skills/agent/core/sheets-write/execute.sh +6 -1
  26. package/config/skills/agent/core/slides-create/SKILL.md +10 -0
  27. package/config/skills/agent/core/slides-create/execute.sh +6 -1
  28. package/config/skills/agent/core/slides-read/SKILL.md +10 -0
  29. package/config/skills/agent/core/slides-read/execute.sh +6 -1
  30. package/config/slack-app-manifest.json +16 -9
  31. package/dist/backend/backend/src/constants.d.ts +18 -4
  32. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  33. package/dist/backend/backend/src/constants.js +16 -4
  34. package/dist/backend/backend/src/constants.js.map +1 -1
  35. package/dist/backend/backend/src/controllers/google/google.controller.d.ts +8 -0
  36. package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
  37. package/dist/backend/backend/src/controllers/google/google.controller.js +137 -37
  38. package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
  39. package/dist/backend/backend/src/controllers/google/google.routes.d.ts +2 -1
  40. package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -1
  41. package/dist/backend/backend/src/controllers/google/google.routes.js +4 -2
  42. package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -1
  43. package/dist/backend/backend/src/controllers/slack/slack-error.utils.d.ts +46 -0
  44. package/dist/backend/backend/src/controllers/slack/slack-error.utils.d.ts.map +1 -0
  45. package/dist/backend/backend/src/controllers/slack/slack-error.utils.js +54 -0
  46. package/dist/backend/backend/src/controllers/slack/slack-error.utils.js.map +1 -0
  47. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  48. package/dist/backend/backend/src/controllers/slack/slack.controller.js +5 -12
  49. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  50. package/dist/backend/backend/src/services/google/google-api.client.d.ts +23 -2
  51. package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/google/google-api.client.js +5 -2
  53. package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
  54. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts +61 -11
  55. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/google/google-workspace-token.service.js +108 -31
  57. package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +27 -4
  60. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  61. package/dist/backend/build-info.json +2 -2
  62. package/dist/cli/backend/src/constants.d.ts +18 -4
  63. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  64. package/dist/cli/backend/src/constants.js +16 -4
  65. package/dist/cli/backend/src/constants.js.map +1 -1
  66. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  67. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +27 -4
  68. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  69. package/frontend/dist/assets/{index-e079a375.js → index-e7785269.js} +267 -267
  70. package/frontend/dist/index.html +1 -1
  71. package/package.json +1 -1
  72. package/packages/crewly-agent/src/runtime/agent-runner.service.test.ts +10 -1
  73. package/packages/crewly-agent/src/runtime/agent-runner.service.ts +179 -3
  74. package/packages/crewly-agent/src/runtime/text-tool-calls.test.ts +144 -0
  75. package/packages/crewly-agent/src/runtime/text-tool-calls.ts +316 -0
  76. package/packages/crewly-agent/src/runtime/text-tool-salvage.test.ts +190 -0
  77. package/packages/crewly-agent/src/runtime/types.ts +6 -0
@@ -231,6 +231,12 @@ api_call() {
231
231
  # Tank: every reply-channel call 404'd for want of this variable).
232
232
  echo '{"warning":"CREWLY_SESSION_NAME is not set in this shell — the request is sent without X-Agent-Session; channel replies and heartbeats will not be attributed to you. Prefix the call with CREWLY_SESSION_NAME=<your session name> or restart the agent."}' >&2
233
233
  fi
234
+ # Which connected Google account the call acts as. One Crewly account can
235
+ # connect several (two Gmail logins, say); without this the backend uses
236
+ # whichever is the default, which is what a single-account install wants.
237
+ if [ -n "${CREWLY_GOOGLE_ACCOUNT:-}" ]; then
238
+ args+=(-H "X-Google-Account: $CREWLY_GOOGLE_ACCOUNT")
239
+ fi
234
240
  [ -n "$body" ] && args+=(-d "$body")
235
241
 
236
242
  local response
@@ -60,3 +60,13 @@ bash execute.sh '{"summary":"Standup","start":"2026-09-21T09:00:00Z","end":"2026
60
60
  Missing `--summary` / `--start` / `--end` → `{"error":"Missing required parameter: …"}` on stderr, exit 1.
61
61
  `{"success":false,"reason":"not_connected","hint":"<connect URL>"}` (exit 1) when the owner has not connected Google Workspace;
62
62
  `reason` `validation` when Google cannot parse a time.
63
+
64
+ ## Choosing a Google account
65
+
66
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
67
+
68
+ ```bash
69
+ bash execute.sh --account work@company.com ...
70
+ ```
71
+
72
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -28,6 +28,7 @@ Options:
28
28
  --description Body text
29
29
  --attendee Attendee email (repeat for several)
30
30
  --calendar Calendar id (default: primary)
31
+ --account Which connected Google account to act as (default: your primary)
31
32
  --help | -h Show this help
32
33
  EOF_USAGE
33
34
  }
@@ -41,6 +42,7 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
41
42
  shift || true
42
43
  fi
43
44
 
45
+ ACCOUNT=""
44
46
  while [[ $# -gt 0 ]]; do
45
47
  case "$1" in
46
48
  --summary|--title) [ $# -ge 2 ] || error_exit "--summary requires a value"; SUMMARY="$2"; shift 2 ;;
@@ -51,6 +53,7 @@ while [[ $# -gt 0 ]]; do
51
53
  --calendar|-c) [ $# -ge 2 ] || error_exit "--calendar requires a value"; CALENDAR="$2"; shift 2 ;;
52
54
  --attendee) [ $# -ge 2 ] || error_exit "--attendee requires a value"
53
55
  ATTENDEES_JSON=$(jq -cn --argjson arr "$ATTENDEES_JSON" --arg v "$2" '$arr + [$v]'); shift 2 ;;
56
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
54
57
  --help|-h) print_usage; exit 0 ;;
55
58
  *) error_exit "Unknown option: $1" ;;
56
59
  esac
@@ -67,7 +70,10 @@ if [ -n "$INPUT_JSON" ]; then
67
70
  if [ "$(printf '%s' "$ATTENDEES_JSON" | jq 'length')" -eq 0 ]; then
68
71
  ATTENDEES_JSON=$(printf '%s' "$INPUT" | jq -c '(.attendees // []) | if type == "string" then split(",") | map(gsub("^\\s+|\\s+$"; "")) | map(select(. != "")) else . end')
69
72
  fi
73
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
70
74
  fi
75
+ # Route the call at one connected Google account; unset means the default.
76
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
71
77
 
72
78
  require_param "summary (--summary)" "$SUMMARY"
73
79
  require_param "start (--start)" "$START"
@@ -56,3 +56,13 @@ bash execute.sh '{"calendarId":"team@group.calendar.google.com","from":"…","to
56
56
 
57
57
  `{"success":false,"reason":"not_connected","hint":"<connect URL>"}` (exit 1)
58
58
  when the owner has not connected Google Workspace.
59
+
60
+ ## Choosing a Google account
61
+
62
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
63
+
64
+ ```bash
65
+ bash execute.sh --account work@company.com ...
66
+ ```
67
+
68
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -24,6 +24,7 @@ Options:
24
24
  --to Upper bound (default: now + 7 days)
25
25
  --calendar Calendar id (default: primary)
26
26
  --max Result cap (default 50, max 250)
27
+ --account Which connected Google account to act as (default: your primary)
27
28
  --help | -h Show this help
28
29
  EOF_USAGE
29
30
  }
@@ -48,12 +49,14 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
48
49
  shift || true
49
50
  fi
50
51
 
52
+ ACCOUNT=""
51
53
  while [[ $# -gt 0 ]]; do
52
54
  case "$1" in
53
55
  --from) [ $# -ge 2 ] || error_exit "--from requires a value"; FROM="$2"; shift 2 ;;
54
56
  --to) [ $# -ge 2 ] || error_exit "--to requires a value"; TO="$2"; shift 2 ;;
55
57
  --calendar|-c) [ $# -ge 2 ] || error_exit "--calendar requires a value"; CALENDAR="$2"; shift 2 ;;
56
58
  --max|-n) [ $# -ge 2 ] || error_exit "--max requires a value"; MAX="$2"; shift 2 ;;
59
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
57
60
  --help|-h) print_usage; exit 0 ;;
58
61
  *) error_exit "Unknown option: $1" ;;
59
62
  esac
@@ -65,7 +68,10 @@ if [ -n "$INPUT_JSON" ]; then
65
68
  [ -z "$TO" ] && TO=$(printf '%s' "$INPUT" | jq -r '.to // .timeMax // empty')
66
69
  [ -z "$CALENDAR" ] && CALENDAR=$(printf '%s' "$INPUT" | jq -r '.calendarId // .calendar // empty')
67
70
  [ -z "$MAX" ] && MAX=$(printf '%s' "$INPUT" | jq -r '.max // empty')
71
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
68
72
  fi
73
+ # Route the call at one connected Google account; unset means the default.
74
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
69
75
 
70
76
  [ -z "$FROM" ] && FROM=$(date -u +%Y-%m-%dT%H:%M:%SZ)
71
77
  [ -z "$TO" ] && TO=$(iso_now_plus_days "$DEFAULT_WINDOW_DAYS")
@@ -48,3 +48,13 @@ bash execute.sh --id "https://docs.google.com/document/d/1AbC…/edit"
48
48
  when the owner has not connected Google Workspace (Settings → Integrations).
49
49
  A `403` with `reason: "google_error"` on a write means the file was not
50
50
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
51
+
52
+ ## Choosing a Google account
53
+
54
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
55
+
56
+ ```bash
57
+ bash execute.sh --account work@company.com ...
58
+ ```
59
+
60
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -20,6 +20,7 @@ Usage:
20
20
 
21
21
  Options:
22
22
  --id Document id (from drive-search or the docs.google.com URL)
23
+ --account Which connected Google account to act as (default: your primary)
23
24
  --help | -h Show this help
24
25
  EOF_USAGE
25
26
  }
@@ -48,10 +49,11 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
48
49
  INPUT_JSON="$1"
49
50
  shift || true
50
51
  fi
51
- ID=""
52
+ ID=""; ACCOUNT=""
52
53
  while [[ $# -gt 0 ]]; do
53
54
  case "$1" in
54
55
  --id) [ $# -ge 2 ] || error_exit "--id requires a value"; ID="$2"; shift 2 ;;
56
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
55
57
  --help|-h) print_usage; exit 0 ;;
56
58
  *) error_exit "Unknown option: $1" ;;
57
59
  esac
@@ -59,7 +61,10 @@ done
59
61
  if [ -n "$INPUT_JSON" ]; then
60
62
  INPUT=$(read_json_input "$INPUT_JSON")
61
63
  [ -z "$ID" ] && ID=$(printf '%s' "$INPUT" | jq -r '.id // .documentId // empty')
64
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
62
65
  fi
66
+ # Route the call at one connected Google account; unset means the default.
67
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
63
68
  # Accept a full docs.google.com URL too.
64
69
  ID=$(printf '%s' "$ID" | sed -E 's#.*/document/d/([^/?]+).*#\1#')
65
70
  require_param "id (--id)" "$ID"
@@ -53,3 +53,13 @@ re-connect Google Workspace once the scope is enabled".
53
53
  when the owner has not connected Google Workspace (Settings → Integrations).
54
54
  A `403` with `reason: "google_error"` on a write means the file was not
55
55
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
56
+
57
+ ## Choosing a Google account
58
+
59
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
60
+
61
+ ```bash
62
+ bash execute.sh --account work@company.com ...
63
+ ```
64
+
65
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -27,6 +27,7 @@ Options:
27
27
  --id Append to this document instead (must be one Crewly created, or the grant needs the Docs scope)
28
28
  --text Body text (plain; blank line = new paragraph)
29
29
  --text-file Read the body from a file
30
+ --account Which connected Google account to act as (default: your primary)
30
31
  --help | -h Show this help
31
32
  EOF_USAGE
32
33
  }
@@ -55,13 +56,14 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
55
56
  INPUT_JSON="$1"
56
57
  shift || true
57
58
  fi
58
- TITLE=""; ID=""; TEXT=""; TEXT_FILE=""
59
+ TITLE=""; ID=""; TEXT=""; TEXT_FILE=""; ACCOUNT=""
59
60
  while [[ $# -gt 0 ]]; do
60
61
  case "$1" in
61
62
  --title) [ $# -ge 2 ] || error_exit "--title requires a value"; TITLE="$2"; shift 2 ;;
62
63
  --id) [ $# -ge 2 ] || error_exit "--id requires a value"; ID="$2"; shift 2 ;;
63
64
  --text) [ $# -ge 2 ] || error_exit "--text requires a value"; TEXT="$2"; shift 2 ;;
64
65
  --text-file) [ $# -ge 2 ] || error_exit "--text-file requires a value"; TEXT_FILE="$2"; shift 2 ;;
66
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
65
67
  --help|-h) print_usage; exit 0 ;;
66
68
  *) error_exit "Unknown option: $1" ;;
67
69
  esac
@@ -71,7 +73,10 @@ if [ -n "$INPUT_JSON" ]; then
71
73
  [ -z "$TITLE" ] && TITLE=$(printf '%s' "$INPUT" | jq -r '.title // empty')
72
74
  [ -z "$ID" ] && ID=$(printf '%s' "$INPUT" | jq -r '.id // .documentId // empty')
73
75
  [ -z "$TEXT" ] && TEXT=$(printf '%s' "$INPUT" | jq -r '.text // .body // empty')
76
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
74
77
  fi
78
+ # Route the call at one connected Google account; unset means the default.
79
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
75
80
  if [ -n "$TEXT_FILE" ]; then
76
81
  [ -f "$TEXT_FILE" ] || error_exit "text file not found: $TEXT_FILE"
77
82
  TEXT=$(cat "$TEXT_FILE")
@@ -51,3 +51,13 @@ Files over 10 MB are refused with a `webViewLink` to open instead.
51
51
  when the owner has not connected Google Workspace (Settings → Integrations).
52
52
  A `403` with `reason: "google_error"` on a write means the file was not
53
53
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
54
+
55
+ ## Choosing a Google account
56
+
57
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
58
+
59
+ ```bash
60
+ bash execute.sh --account work@company.com ...
61
+ ```
62
+
63
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -21,6 +21,7 @@ Usage:
21
21
  Options:
22
22
  --id Drive file id (required; from drive-search)
23
23
  --out Save the content to this path instead of printing it (binary files are decoded)
24
+ --account Which connected Google account to act as (default: your primary)
24
25
  --help | -h Show this help
25
26
  EOF_USAGE
26
27
  }
@@ -49,11 +50,12 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
49
50
  INPUT_JSON="$1"
50
51
  shift || true
51
52
  fi
52
- ID=""; OUT_PATH=""
53
+ ID=""; OUT_PATH=""; ACCOUNT=""
53
54
  while [[ $# -gt 0 ]]; do
54
55
  case "$1" in
55
56
  --id) [ $# -ge 2 ] || error_exit "--id requires a value"; ID="$2"; shift 2 ;;
56
57
  --out|-o) [ $# -ge 2 ] || error_exit "--out requires a value"; OUT_PATH="$2"; shift 2 ;;
58
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
57
59
  --help|-h) print_usage; exit 0 ;;
58
60
  *) error_exit "Unknown option: $1" ;;
59
61
  esac
@@ -62,7 +64,10 @@ if [ -n "$INPUT_JSON" ]; then
62
64
  INPUT=$(read_json_input "$INPUT_JSON")
63
65
  [ -z "$ID" ] && ID=$(printf '%s' "$INPUT" | jq -r '.id // .fileId // empty')
64
66
  [ -z "$OUT_PATH" ] && OUT_PATH=$(printf '%s' "$INPUT" | jq -r '.out // empty')
67
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
65
68
  fi
69
+ # Route the call at one connected Google account; unset means the default.
70
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
66
71
  require_param "id (--id)" "$ID"
67
72
  RESPONSE=$(call GET "/google/drive/files/$(uri "$ID")/content") || { printf '%s\n' "$RESPONSE"; exit 1; }
68
73
  if printf '%s' "$RESPONSE" | jq -e '.truncated == true' >/dev/null 2>&1; then printf '%s\n' "$RESPONSE"; exit 0; fi
@@ -53,3 +53,13 @@ else → `drive-read`.
53
53
  when the owner has not connected Google Workspace (Settings → Integrations).
54
54
  A `403` with `reason: "google_error"` on a write means the file was not
55
55
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
56
+
57
+ ## Choosing a Google account
58
+
59
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
60
+
61
+ ```bash
62
+ bash execute.sh --account work@company.com ...
63
+ ```
64
+
65
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -23,6 +23,7 @@ Options:
23
23
  --mime Restrict to a type: doc | sheet | slides | pdf | folder | any MIME type
24
24
  --folder Restrict to a Drive folder id
25
25
  --max Result cap (default 20, max 100)
26
+ --account Which connected Google account to act as (default: your primary)
26
27
  --help | -h Show this help
27
28
  EOF_USAGE
28
29
  }
@@ -51,13 +52,14 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
51
52
  INPUT_JSON="$1"
52
53
  shift || true
53
54
  fi
54
- QUERY=""; MIME=""; FOLDER=""; MAX=""
55
+ QUERY=""; MIME=""; FOLDER=""; MAX=""; ACCOUNT=""
55
56
  while [[ $# -gt 0 ]]; do
56
57
  case "$1" in
57
58
  --query|-q) [ $# -ge 2 ] || error_exit "--query requires a value"; QUERY="$2"; shift 2 ;;
58
59
  --mime) [ $# -ge 2 ] || error_exit "--mime requires a value"; MIME="$2"; shift 2 ;;
59
60
  --folder) [ $# -ge 2 ] || error_exit "--folder requires a value"; FOLDER="$2"; shift 2 ;;
60
61
  --max|-n) [ $# -ge 2 ] || error_exit "--max requires a value"; MAX="$2"; shift 2 ;;
62
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
61
63
  --help|-h) print_usage; exit 0 ;;
62
64
  *) error_exit "Unknown option: $1" ;;
63
65
  esac
@@ -68,7 +70,10 @@ if [ -n "$INPUT_JSON" ]; then
68
70
  [ -z "$MIME" ] && MIME=$(printf '%s' "$INPUT" | jq -r '.mime // .mimeType // empty')
69
71
  [ -z "$FOLDER" ] && FOLDER=$(printf '%s' "$INPUT" | jq -r '.folder // .folderId // empty')
70
72
  [ -z "$MAX" ] && MAX=$(printf '%s' "$INPUT" | jq -r '.max // empty')
73
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
71
74
  fi
75
+ # Route the call at one connected Google account; unset means the default.
76
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
72
77
  case "$MIME" in
73
78
  doc|docs) MIME="application/vnd.google-apps.document" ;;
74
79
  sheet|sheets) MIME="application/vnd.google-apps.spreadsheet" ;;
@@ -52,3 +52,13 @@ permissions are the owner's — this skill does not change sharing).
52
52
  when the owner has not connected Google Workspace (Settings → Integrations).
53
53
  A `403` with `reason: "google_error"` on a write means the file was not
54
54
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
55
+
56
+ ## Choosing a Google account
57
+
58
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
59
+
60
+ ```bash
61
+ bash execute.sh --account work@company.com ...
62
+ ```
63
+
64
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -28,6 +28,7 @@ Options:
28
28
  --mime MIME type override
29
29
  --folder Destination folder id (default: My Drive root)
30
30
  --convert Convert on upload: doc | sheet | slides (e.g. a .docx/.csv/.pptx becomes a Google-native file)
31
+ --account Which connected Google account to act as (default: your primary)
31
32
  --help | -h Show this help
32
33
  EOF_USAGE
33
34
  }
@@ -56,7 +57,7 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
56
57
  INPUT_JSON="$1"
57
58
  shift || true
58
59
  fi
59
- FILE=""; NAME=""; TEXT=""; MIME=""; FOLDER=""; CONVERT=""
60
+ FILE=""; NAME=""; TEXT=""; MIME=""; FOLDER=""; CONVERT=""; ACCOUNT=""
60
61
  while [[ $# -gt 0 ]]; do
61
62
  case "$1" in
62
63
  --path|-p) [ $# -ge 2 ] || error_exit "--path requires a value"; FILE="$2"; shift 2 ;;
@@ -65,6 +66,7 @@ while [[ $# -gt 0 ]]; do
65
66
  --mime) [ $# -ge 2 ] || error_exit "--mime requires a value"; MIME="$2"; shift 2 ;;
66
67
  --folder) [ $# -ge 2 ] || error_exit "--folder requires a value"; FOLDER="$2"; shift 2 ;;
67
68
  --convert) [ $# -ge 2 ] || error_exit "--convert requires a value"; CONVERT="$2"; shift 2 ;;
69
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
68
70
  --help|-h) print_usage; exit 0 ;;
69
71
  *) error_exit "Unknown option: $1" ;;
70
72
  esac
@@ -77,7 +79,10 @@ if [ -n "$INPUT_JSON" ]; then
77
79
  [ -z "$MIME" ] && MIME=$(printf '%s' "$INPUT" | jq -r '.mime // .mimeType // empty')
78
80
  [ -z "$FOLDER" ] && FOLDER=$(printf '%s' "$INPUT" | jq -r '.folder // .folderId // empty')
79
81
  [ -z "$CONVERT" ] && CONVERT=$(printf '%s' "$INPUT" | jq -r '.convert // .convertTo // empty')
82
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
80
83
  fi
84
+ # Route the call at one connected Google account; unset means the default.
85
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
81
86
  case "$CONVERT" in
82
87
  doc|docs) CONVERT="application/vnd.google-apps.document" ;;
83
88
  sheet|sheets) CONVERT="application/vnd.google-apps.spreadsheet" ;;
@@ -59,3 +59,13 @@ pass `threadId` and `messageId` to `gmail-send`.
59
59
  `{"success":false,"reason":"not_connected","hint":"<connect URL>"}` (exit 1)
60
60
  when the owner has not connected Google Workspace; `reason` `google_error`
61
61
  with a 404 message when the id is unknown.
62
+
63
+ ## Choosing a Google account
64
+
65
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
66
+
67
+ ```bash
68
+ bash execute.sh --account work@company.com ...
69
+ ```
70
+
71
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -20,6 +20,7 @@ Usage:
20
20
 
21
21
  Options:
22
22
  --id | -i Gmail message id (required; from gmail-search)
23
+ --account Which connected Google account to act as (default: your primary)
23
24
  --help | -h Show this help
24
25
  EOF_USAGE
25
26
  }
@@ -32,9 +33,11 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
32
33
  shift || true
33
34
  fi
34
35
 
36
+ ACCOUNT=""
35
37
  while [[ $# -gt 0 ]]; do
36
38
  case "$1" in
37
39
  --id|-i) [ $# -ge 2 ] || error_exit "--id requires a value"; ID="$2"; shift 2 ;;
40
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
38
41
  --help|-h) print_usage; exit 0 ;;
39
42
  *) error_exit "Unknown option: $1" ;;
40
43
  esac
@@ -43,7 +46,10 @@ done
43
46
  if [ -n "$INPUT_JSON" ]; then
44
47
  INPUT=$(read_json_input "$INPUT_JSON")
45
48
  [ -z "$ID" ] && ID=$(printf '%s' "$INPUT" | jq -r '.id // .messageId // empty')
49
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
46
50
  fi
51
+ # Route the call at one connected Google account; unset means the default.
52
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
47
53
 
48
54
  require_param "id (--id)" "$ID"
49
55
 
@@ -62,3 +62,13 @@ Use the `id` with `gmail-read` to get the body; `threadId` + `gmail-read`'s
62
62
  `{"success":false,"reason":"not_connected","hint":"<connect URL>"}` — the
63
63
  owner has not connected Google Workspace yet; hand them the hint URL (or
64
64
  point them to Settings → Integrations → Google Workspace). Exit code 1.
65
+
66
+ ## Choosing a Google account
67
+
68
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
69
+
70
+ ```bash
71
+ bash execute.sh --account work@company.com ...
72
+ ```
73
+
74
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -23,6 +23,7 @@ Options:
23
23
  --query | -q Gmail search query (required)
24
24
  --max | -n Result cap (default 20, max 100)
25
25
  --help | -h Show this help
26
+ --account Which connected Google account to act as (default: your primary)
26
27
  EOF_USAGE
27
28
  }
28
29
 
@@ -35,10 +36,12 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
35
36
  shift || true
36
37
  fi
37
38
 
39
+ ACCOUNT=""
38
40
  while [[ $# -gt 0 ]]; do
39
41
  case "$1" in
40
42
  --query|-q) [ $# -ge 2 ] || error_exit "--query requires a value"; QUERY="$2"; shift 2 ;;
41
43
  --max|-n) [ $# -ge 2 ] || error_exit "--max requires a value"; MAX="$2"; shift 2 ;;
44
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
42
45
  --help|-h) print_usage; exit 0 ;;
43
46
  *) error_exit "Unknown option: $1" ;;
44
47
  esac
@@ -48,7 +51,10 @@ if [ -n "$INPUT_JSON" ]; then
48
51
  INPUT=$(read_json_input "$INPUT_JSON")
49
52
  [ -z "$QUERY" ] && QUERY=$(printf '%s' "$INPUT" | jq -r '.query // .q // empty')
50
53
  [ -z "$MAX" ] && MAX=$(printf '%s' "$INPUT" | jq -r '.max // empty')
54
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
51
55
  fi
56
+ # Route the call at one connected Google account; unset means the default.
57
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
52
58
 
53
59
  require_param "query (--query)" "$QUERY"
54
60
 
@@ -67,3 +67,13 @@ Dry run: `{"success":true,"dryRun":true,"preview":"To: …\nSubject: …\n\n<bod
67
67
 
68
68
  Missing `--to` / `--subject` / body → `{"error":"Missing required parameter: …"}` on stderr, exit 1, nothing sent.
69
69
  `{"success":false,"reason":"not_connected","hint":"<connect URL>"}` (exit 1) when the owner has not connected Google Workspace.
70
+
71
+ ## Choosing a Google account
72
+
73
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
74
+
75
+ ```bash
76
+ bash execute.sh --account work@company.com ...
77
+ ```
78
+
79
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -31,6 +31,7 @@ Options:
31
31
  --thread-id Reply inside this Gmail thread
32
32
  --in-reply-to Message-ID being answered (sets In-Reply-To / References)
33
33
  --dry-run Preview only; nothing is sent (also: CREWLY_GMAIL_SEND_DRY_RUN=1)
34
+ --account Which connected Google account to act as (default: your primary)
34
35
  --help | -h Show this help
35
36
  EOF_USAGE
36
37
  }
@@ -44,6 +45,7 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
44
45
  shift || true
45
46
  fi
46
47
 
48
+ ACCOUNT=""
47
49
  while [[ $# -gt 0 ]]; do
48
50
  case "$1" in
49
51
  --to) [ $# -ge 2 ] || error_exit "--to requires a value"; TO="$2"; shift 2 ;;
@@ -54,6 +56,7 @@ while [[ $# -gt 0 ]]; do
54
56
  --thread-id) [ $# -ge 2 ] || error_exit "--thread-id requires a value"; THREAD_ID="$2"; shift 2 ;;
55
57
  --in-reply-to) [ $# -ge 2 ] || error_exit "--in-reply-to requires a value"; IN_REPLY_TO="$2"; shift 2 ;;
56
58
  --dry-run) DRY_RUN=1; shift ;;
59
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
57
60
  --help|-h) print_usage; exit 0 ;;
58
61
  *) error_exit "Unknown option: $1" ;;
59
62
  esac
@@ -68,7 +71,10 @@ if [ -n "$INPUT_JSON" ]; then
68
71
  [ -z "$THREAD_ID" ] && THREAD_ID=$(printf '%s' "$INPUT" | jq -r '.threadId // empty')
69
72
  [ -z "$IN_REPLY_TO" ] && IN_REPLY_TO=$(printf '%s' "$INPUT" | jq -r '.inReplyTo // empty')
70
73
  [ -z "$DRY_RUN" ] && DRY_RUN=$(printf '%s' "$INPUT" | jq -r 'if .dryRun == true then "1" else "" end')
74
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
71
75
  fi
76
+ # Route the call at one connected Google account; unset means the default.
77
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
72
78
 
73
79
  if [ -n "$TEXT_FILE" ]; then
74
80
  [ -f "$TEXT_FILE" ] || error_exit "text file not found: $TEXT_FILE"
@@ -51,3 +51,13 @@ come back as numbers, dates as the sheet's formatted string.
51
51
  when the owner has not connected Google Workspace (Settings → Integrations).
52
52
  A `403` with `reason: "google_error"` on a write means the file was not
53
53
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
54
+
55
+ ## Choosing a Google account
56
+
57
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
58
+
59
+ ```bash
60
+ bash execute.sh --account work@company.com ...
61
+ ```
62
+
63
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -24,6 +24,7 @@ Options:
24
24
  --id Spreadsheet id (or its URL)
25
25
  --range A1 range, e.g. "Q3!A1:D50" (default A1:Z1000 on the first tab)
26
26
  --info Print the title and tab names instead of values
27
+ --account Which connected Google account to act as (default: your primary)
27
28
  --help | -h Show this help
28
29
  EOF_USAGE
29
30
  }
@@ -52,12 +53,13 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
52
53
  INPUT_JSON="$1"
53
54
  shift || true
54
55
  fi
55
- ID=""; RANGE=""; INFO=""
56
+ ID=""; RANGE=""; INFO=""; ACCOUNT=""
56
57
  while [[ $# -gt 0 ]]; do
57
58
  case "$1" in
58
59
  --id) [ $# -ge 2 ] || error_exit "--id requires a value"; ID="$2"; shift 2 ;;
59
60
  --range) [ $# -ge 2 ] || error_exit "--range requires a value"; RANGE="$2"; shift 2 ;;
60
61
  --info) INFO=1; shift ;;
62
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
61
63
  --help|-h) print_usage; exit 0 ;;
62
64
  *) error_exit "Unknown option: $1" ;;
63
65
  esac
@@ -67,7 +69,10 @@ if [ -n "$INPUT_JSON" ]; then
67
69
  [ -z "$ID" ] && ID=$(printf '%s' "$INPUT" | jq -r '.id // .spreadsheetId // empty')
68
70
  [ -z "$RANGE" ] && RANGE=$(printf '%s' "$INPUT" | jq -r '.range // empty')
69
71
  [ -z "$INFO" ] && INFO=$(printf '%s' "$INPUT" | jq -r 'if .info == true then "1" else "" end')
72
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
70
73
  fi
74
+ # Route the call at one connected Google account; unset means the default.
75
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
71
76
  ID=$(printf '%s' "$ID" | sed -E 's#.*/spreadsheets/d/([^/?]+).*#\1#')
72
77
  require_param "id (--id)" "$ID"
73
78
  if [ -n "$INFO" ]; then
@@ -54,3 +54,13 @@ the owner made outside Crewly needs the Sheets write scope (`403 google_error`).
54
54
  when the owner has not connected Google Workspace (Settings → Integrations).
55
55
  A `403` with `reason: "google_error"` on a write means the file was not
56
56
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
57
+
58
+ ## Choosing a Google account
59
+
60
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
61
+
62
+ ```bash
63
+ bash execute.sh --account work@company.com ...
64
+ ```
65
+
66
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
@@ -30,6 +30,7 @@ Options:
30
30
  --csv-file Rows from a CSV file (simple: comma-separated, quotes stripped)
31
31
  --range A1 anchor: the table to append to, or the top-left cell to overwrite (default A1)
32
32
  --mode append (default) | update
33
+ --account Which connected Google account to act as (default: your primary)
33
34
  --help | -h Show this help
34
35
  EOF_USAGE
35
36
  }
@@ -58,7 +59,7 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
58
59
  INPUT_JSON="$1"
59
60
  shift || true
60
61
  fi
61
- TITLE=""; SHEET=""; ID=""; ROWS=""; CSV_FILE=""; RANGE=""; MODE=""
62
+ TITLE=""; SHEET=""; ID=""; ROWS=""; CSV_FILE=""; RANGE=""; MODE=""; ACCOUNT=""
62
63
  while [[ $# -gt 0 ]]; do
63
64
  case "$1" in
64
65
  --title) [ $# -ge 2 ] || error_exit "--title requires a value"; TITLE="$2"; shift 2 ;;
@@ -68,6 +69,7 @@ while [[ $# -gt 0 ]]; do
68
69
  --csv-file) [ $# -ge 2 ] || error_exit "--csv-file requires a value"; CSV_FILE="$2"; shift 2 ;;
69
70
  --range) [ $# -ge 2 ] || error_exit "--range requires a value"; RANGE="$2"; shift 2 ;;
70
71
  --mode) [ $# -ge 2 ] || error_exit "--mode requires a value"; MODE="$2"; shift 2 ;;
72
+ --account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
71
73
  --help|-h) print_usage; exit 0 ;;
72
74
  *) error_exit "Unknown option: $1" ;;
73
75
  esac
@@ -80,7 +82,10 @@ if [ -n "$INPUT_JSON" ]; then
80
82
  [ -z "$ROWS" ] && ROWS=$(printf '%s' "$INPUT" | jq -c '.rows // empty')
81
83
  [ -z "$RANGE" ] && RANGE=$(printf '%s' "$INPUT" | jq -r '.range // empty')
82
84
  [ -z "$MODE" ] && MODE=$(printf '%s' "$INPUT" | jq -r '.mode // empty')
85
+ [ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
83
86
  fi
87
+ # Route the call at one connected Google account; unset means the default.
88
+ [ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
84
89
  if [ -n "$CSV_FILE" ]; then
85
90
  [ -f "$CSV_FILE" ] || error_exit "csv file not found: $CSV_FILE"
86
91
  ROWS=$(python3 -c 'import csv,json,sys; print(json.dumps(list(csv.reader(open(sys.argv[1], newline="")))))' "$CSV_FILE")
@@ -59,3 +59,13 @@ For designed decks (brand templates, images, video) use the Canva skills.
59
59
  when the owner has not connected Google Workspace (Settings → Integrations).
60
60
  A `403` with `reason: "google_error"` on a write means the file was not
61
61
  created by Crewly — the grant only edits files Crewly made (`drive.file`).
62
+
63
+ ## Choosing a Google account
64
+
65
+ Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
66
+
67
+ ```bash
68
+ bash execute.sh --account work@company.com ...
69
+ ```
70
+
71
+ The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.