crewly 1.5.22 → 1.6.0

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 (112) hide show
  1. package/config/roles/orchestrator/prompt.md +182 -25
  2. package/config/skills/agent/core/cancel-followup/SKILL.md +38 -0
  3. package/config/skills/agent/core/cancel-followup/execute.sh +111 -0
  4. package/config/skills/agent/core/cancel-followup/execute.test.sh +42 -0
  5. package/config/skills/agent/core/list-my-followups/SKILL.md +36 -0
  6. package/config/skills/agent/core/list-my-followups/execute.sh +93 -0
  7. package/config/skills/agent/core/list-my-followups/execute.test.sh +41 -0
  8. package/config/skills/agent/core/schedule-followup/SKILL.md +53 -0
  9. package/config/skills/agent/core/schedule-followup/execute.sh +195 -0
  10. package/config/skills/agent/core/schedule-followup/execute.test.sh +48 -0
  11. package/config/skills/agent/core/watch-for-event/SKILL.md +60 -0
  12. package/config/skills/agent/core/watch-for-event/execute.sh +177 -0
  13. package/config/skills/agent/core/watch-for-event/execute.test.sh +43 -0
  14. package/config/skills/orchestrator/credential-manager/SKILL.md +218 -0
  15. package/config/skills/orchestrator/credential-manager/execute.sh +166 -0
  16. package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts +80 -0
  17. package/dist/backend/backend/src/controllers/credentials/credentials.controller.d.ts.map +1 -0
  18. package/dist/backend/backend/src/controllers/credentials/credentials.controller.js +365 -0
  19. package/dist/backend/backend/src/controllers/credentials/credentials.controller.js.map +1 -0
  20. package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts +26 -0
  21. package/dist/backend/backend/src/controllers/credentials/credentials.routes.d.ts.map +1 -0
  22. package/dist/backend/backend/src/controllers/credentials/credentials.routes.js +40 -0
  23. package/dist/backend/backend/src/controllers/credentials/credentials.routes.js.map +1 -0
  24. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +23 -14
  25. package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
  26. package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts +3 -1
  27. package/dist/backend/backend/src/scripts/backfill-mission-priority.d.ts.map +1 -1
  28. package/dist/backend/backend/src/scripts/backfill-mission-priority.js +16 -4
  29. package/dist/backend/backend/src/scripts/backfill-mission-priority.js.map +1 -1
  30. package/dist/backend/backend/src/services/browser/browser-proxy.service.js +1 -1
  31. package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
  32. package/dist/backend/backend/src/services/credential/credential-store.service.d.ts +161 -0
  33. package/dist/backend/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
  34. package/dist/backend/backend/src/services/credential/credential-store.service.js +298 -0
  35. package/dist/backend/backend/src/services/credential/credential-store.service.js.map +1 -0
  36. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
  37. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
  38. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
  39. package/dist/backend/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
  40. package/dist/backend/backend/src/services/project/task.service.d.ts +18 -2
  41. package/dist/backend/backend/src/services/project/task.service.d.ts.map +1 -1
  42. package/dist/backend/backend/src/services/project/task.service.js +69 -53
  43. package/dist/backend/backend/src/services/project/task.service.js.map +1 -1
  44. package/dist/backend/backend/src/services/v3/contract-matcher.d.ts +20 -0
  45. package/dist/backend/backend/src/services/v3/contract-matcher.d.ts.map +1 -0
  46. package/dist/backend/backend/src/services/v3/contract-matcher.js +33 -0
  47. package/dist/backend/backend/src/services/v3/contract-matcher.js.map +1 -0
  48. package/dist/backend/backend/src/services/v3/escalation.service.d.ts +20 -1
  49. package/dist/backend/backend/src/services/v3/escalation.service.d.ts.map +1 -1
  50. package/dist/backend/backend/src/services/v3/escalation.service.js +97 -28
  51. package/dist/backend/backend/src/services/v3/escalation.service.js.map +1 -1
  52. package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts +6 -4
  53. package/dist/backend/backend/src/services/v3/service-contract-gate.service.d.ts.map +1 -1
  54. package/dist/backend/backend/src/services/v3/service-contract-gate.service.js +18 -28
  55. package/dist/backend/backend/src/services/v3/service-contract-gate.service.js.map +1 -1
  56. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js +14 -9
  58. package/dist/backend/backend/src/services/v3/team-trigger-reconciler.service.js.map +1 -1
  59. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts +34 -1
  60. package/dist/backend/backend/src/services/v3/trigger-engine.service.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/v3/trigger-engine.service.js +115 -5
  62. package/dist/backend/backend/src/services/v3/trigger-engine.service.js.map +1 -1
  63. package/dist/backend/backend/src/types/credential.types.d.ts +185 -0
  64. package/dist/backend/backend/src/types/credential.types.d.ts.map +1 -0
  65. package/dist/backend/backend/src/types/credential.types.js +76 -0
  66. package/dist/backend/backend/src/types/credential.types.js.map +1 -0
  67. package/dist/backend/backend/src/utils/encryption.utils.d.ts +57 -0
  68. package/dist/backend/backend/src/utils/encryption.utils.d.ts.map +1 -0
  69. package/dist/backend/backend/src/utils/encryption.utils.js +162 -0
  70. package/dist/backend/backend/src/utils/encryption.utils.js.map +1 -0
  71. package/dist/cli/backend/src/services/credential/credential-store.service.d.ts +161 -0
  72. package/dist/cli/backend/src/services/credential/credential-store.service.d.ts.map +1 -0
  73. package/dist/cli/backend/src/services/credential/credential-store.service.js +298 -0
  74. package/dist/cli/backend/src/services/credential/credential-store.service.js.map +1 -0
  75. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts +117 -0
  76. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.d.ts.map +1 -0
  77. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js +293 -0
  78. package/dist/cli/backend/src/services/credential/helpers/gemini-cli-workspace.helper.js.map +1 -0
  79. package/dist/cli/backend/src/services/settings/settings.service.d.ts +168 -0
  80. package/dist/cli/backend/src/services/settings/settings.service.d.ts.map +1 -0
  81. package/dist/cli/backend/src/services/settings/settings.service.js +312 -0
  82. package/dist/cli/backend/src/services/settings/settings.service.js.map +1 -0
  83. package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts +159 -0
  84. package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -0
  85. package/dist/cli/backend/src/services/skill/skill-executor.service.js +626 -0
  86. package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -0
  87. package/dist/cli/backend/src/services/skill/skill.service.d.ts +273 -0
  88. package/dist/cli/backend/src/services/skill/skill.service.d.ts.map +1 -0
  89. package/dist/cli/backend/src/services/skill/skill.service.js +655 -0
  90. package/dist/cli/backend/src/services/skill/skill.service.js.map +1 -0
  91. package/dist/cli/backend/src/types/credential.types.d.ts +185 -0
  92. package/dist/cli/backend/src/types/credential.types.d.ts.map +1 -0
  93. package/dist/cli/backend/src/types/credential.types.js +76 -0
  94. package/dist/cli/backend/src/types/credential.types.js.map +1 -0
  95. package/dist/cli/backend/src/utils/encryption.utils.d.ts +57 -0
  96. package/dist/cli/backend/src/utils/encryption.utils.d.ts.map +1 -0
  97. package/dist/cli/backend/src/utils/encryption.utils.js +162 -0
  98. package/dist/cli/backend/src/utils/encryption.utils.js.map +1 -0
  99. package/dist/cli/backend/src/utils/skill-md-parser.d.ts +38 -0
  100. package/dist/cli/backend/src/utils/skill-md-parser.d.ts.map +1 -0
  101. package/dist/cli/backend/src/utils/skill-md-parser.js +47 -0
  102. package/dist/cli/backend/src/utils/skill-md-parser.js.map +1 -0
  103. package/frontend/dist/assets/{index-dc92ab64.css → index-6aaa0630.css} +1 -1
  104. package/frontend/dist/assets/{index-76d76633.js → index-9e6d97d1.js} +334 -328
  105. package/frontend/dist/index.html +2 -2
  106. package/package.json +1 -1
  107. package/config/experts/empathetic-resolver/expert.json +0 -11
  108. package/config/experts/empathetic-resolver.md +0 -32
  109. package/config/experts/pragmatic-architect/expert.json +0 -11
  110. package/config/experts/pragmatic-architect.md +0 -32
  111. package/config/experts/viral-alchemist/expert.json +0 -11
  112. package/config/experts/viral-alchemist.md +0 -32
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: Credential Manager
3
+ description: "Manage workspace credentials (Google OAuth accounts, service API keys) that skills use to call third-party services on the user's behalf. Primary flow for remote users over Slack: start-google-oauth returns a link the user clicks on their device; they sign in and paste the resulting JSON back, then complete-google-oauth saves the credential. Supports multi-account."
4
+ version: 1.1.0
5
+ category: management
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - orchestrator
9
+ triggers:
10
+ - add gmail account
11
+ - add google account
12
+ - add api key
13
+ - list credentials
14
+ - show credentials
15
+ - what credentials
16
+ - delete credential
17
+ - revoke credential
18
+ - connect google
19
+ - connect gmail
20
+ - read gmail
21
+ - check unread emails
22
+ - unread messages
23
+ - my inbox
24
+ tags:
25
+ - credentials
26
+ - oauth
27
+ - gmail
28
+ - google
29
+ - api-key
30
+ execution:
31
+ type: script
32
+ script:
33
+ file: execute.sh
34
+ interpreter: bash
35
+ timeoutMs: 30000
36
+ ---
37
+
38
+ # Credential Manager
39
+
40
+ Manages the workspace's credential store used by skills. Supports Google OAuth
41
+ (multi-account) and API keys.
42
+
43
+ **Credential values are never returned** — only metadata (id, name, email,
44
+ scopes). The actual tokens stay encrypted on disk and are only exposed to
45
+ skills at execution time via per-slot env vars.
46
+
47
+ ## Primary flow: Remote user via Slack
48
+
49
+ The user is on Slack and cannot run terminal commands. Use this flow:
50
+
51
+ ### 1. Start — return a sign-in link
52
+
53
+ ```bash
54
+ bash execute.sh '{"action":"start-google-oauth"}'
55
+ ```
56
+
57
+ Returns:
58
+ ```json
59
+ {
60
+ "success": true,
61
+ "authUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&scope=...",
62
+ "instructions": "Send authUrl to the user. They click it..."
63
+ }
64
+ ```
65
+
66
+ **Send the `authUrl` to the user**. Tell them:
67
+ > Please click this link, sign in with the Google account you want to add,
68
+ > then copy the entire JSON block shown on the success page and paste it back here.
69
+
70
+ The URL opens on their device (phone, laptop, whatever). Google shows the
71
+ consent screen ("Google Workspace Extension for Gemini CLI wants to access...").
72
+ After they approve, they land on a page titled **"Success! Credentials Ready"**
73
+ with a JSON block like:
74
+
75
+ ```json
76
+ {
77
+ "refresh_token": "1//...",
78
+ "scope": "...",
79
+ "token_type": "Bearer",
80
+ "access_token": "ya29...",
81
+ "expiry_date": 1234567890123
82
+ }
83
+ ```
84
+
85
+ ### 2. Complete — save the credential
86
+
87
+ After the user pastes the JSON back, call:
88
+
89
+ ```bash
90
+ bash execute.sh '{
91
+ "action":"complete-google-oauth",
92
+ "name":"info-steam-fun",
93
+ "credentialsJson": {"refresh_token":"...","access_token":"...","scope":"...","token_type":"Bearer","expiry_date":...}
94
+ }'
95
+ ```
96
+
97
+ Returns:
98
+ ```json
99
+ {
100
+ "success": true,
101
+ "credential": {
102
+ "id": "cred-abc123...",
103
+ "name": "info-steam-fun",
104
+ "type": "google-oauth",
105
+ "provider": "google",
106
+ "helper": "gemini-cli-workspace",
107
+ "accountEmail": "info@steam-fun.com",
108
+ "scopes": [...]
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### 3. Repeat for more accounts
114
+
115
+ Just call `start-google-oauth` → send new URL → `complete-google-oauth` again
116
+ with a different name. No extension state to clear (headless mode never
117
+ touches it).
118
+
119
+ ---
120
+
121
+ ## Developer flow (on-box only)
122
+
123
+ If you're running on the same machine as Crewly AND have the Gemini CLI
124
+ Workspace extension set up for interactive login, these still work:
125
+
126
+ ### `import-google`
127
+
128
+ Import the extension's currently-active login:
129
+
130
+ ```bash
131
+ bash execute.sh '{"action":"import-google","name":"info-steam-fun"}'
132
+ ```
133
+
134
+ Precondition: user has run `GEMINI_CLI_WORKSPACE_FORCE_FILE_STORAGE=true gemini`
135
+ and completed sign-in, leaving tokens in the extension's file cache.
136
+
137
+ ### `clear-gemini-cli`
138
+
139
+ Delete the extension's cached token file so the next local login captures
140
+ a fresh account:
141
+
142
+ ```bash
143
+ bash execute.sh '{"action":"clear-gemini-cli"}'
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Other actions
149
+
150
+ ### `list`
151
+
152
+ List credentials (filter optional).
153
+
154
+ ```bash
155
+ bash execute.sh '{"action":"list"}'
156
+ bash execute.sh '{"action":"list","type":"google-oauth"}'
157
+ bash execute.sh '{"action":"list","provider":"gemini"}'
158
+ ```
159
+
160
+ ### `add-api-key`
161
+
162
+ ```bash
163
+ bash execute.sh '{"action":"add-api-key","name":"gemini-main","provider":"gemini","value":"AIza..."}'
164
+ ```
165
+
166
+ ### `delete`
167
+
168
+ ```bash
169
+ bash execute.sh '{"action":"delete","id":"cred-abc123..."}'
170
+ ```
171
+
172
+ ### `read-gmail`
173
+
174
+ Read unread emails from the named Google account:
175
+
176
+ ```bash
177
+ bash execute.sh '{"action":"read-gmail","name":"info-steam-fun"}'
178
+ bash execute.sh '{"action":"read-gmail","name":"personal-gmail","maxResults":5}'
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Multi-account example (remote Slack user)
184
+
185
+ ```
186
+ User: "Add info@steam-fun.com to Crewly"
187
+ Orchestrator:
188
+ 1. bash execute.sh '{"action":"start-google-oauth"}'
189
+ 2. Gets authUrl back.
190
+ 3. Replies in Slack:
191
+ "Click this to sign in with info@steam-fun.com: <authUrl>
192
+ After signing in, copy the JSON block from the 'Success!' page
193
+ and paste it back here."
194
+
195
+ User: (clicks on phone, signs in, pastes JSON)
196
+ {"refresh_token":"...","access_token":"...","scope":"...","token_type":"Bearer","expiry_date":...}
197
+
198
+ Orchestrator:
199
+ 1. bash execute.sh '{"action":"complete-google-oauth","name":"info-steam-fun","credentialsJson":<pasted JSON>}'
200
+ 2. Replies: "✓ Added info-steam-fun (info@steam-fun.com)."
201
+
202
+ User: "Now add my personal gmail"
203
+ Orchestrator:
204
+ repeats start → URL → user pastes → complete, with name=personal-gmail.
205
+
206
+ User: "What's unread in info's inbox?"
207
+ Orchestrator:
208
+ bash execute.sh '{"action":"read-gmail","name":"info-steam-fun"}'
209
+ → returns summary of unread emails.
210
+ ```
211
+
212
+ ## Errors
213
+
214
+ - `name is required` / `credentialsJson is required` — missing fields in complete action
215
+ - `credentialsJson is not valid JSON` — user pasted something other than the JSON block
216
+ - `credentialsJson is missing access_token or refresh_token` — user pasted a partial JSON
217
+ - `No google-oauth credential found with name '...'` — `read-gmail` lookup failed; try `list`
218
+ - `Credential '...' is revoked` — the refresh token was revoked by the user or Google; re-run the OAuth flow
@@ -0,0 +1,166 @@
1
+ #!/bin/bash
2
+ # Credential Manager — wraps /api/credentials REST endpoints so the
3
+ # orchestrator can manage Google OAuth accounts and API keys on user request.
4
+ set -euo pipefail
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "${SCRIPT_DIR}/../_common/lib.sh"
7
+
8
+ INPUT=$(read_json_input "${1:-}")
9
+ if [ -z "$INPUT" ]; then
10
+ error_exit "Usage: execute.sh '{\"action\":\"list|import-google|clear-gemini-cli|add-api-key|delete|read-gmail\", ...}'"
11
+ fi
12
+
13
+ ACTION=$(printf '%s' "$INPUT" | jq -r '.action // empty')
14
+ require_param "action" "$ACTION"
15
+
16
+ case "$ACTION" in
17
+ list)
18
+ TYPE=$(printf '%s' "$INPUT" | jq -r '.type // empty')
19
+ PROVIDER=$(printf '%s' "$INPUT" | jq -r '.provider // empty')
20
+ RESP=$(api_call GET "/credentials")
21
+ # Filter client-side
22
+ printf '%s' "$RESP" | jq --arg type "$TYPE" --arg provider "$PROVIDER" '
23
+ .data
24
+ | map(select($type == "" or .type == $type))
25
+ | map(select($provider == "" or .provider == $provider))
26
+ | map({id, name, type, provider, helper, accountEmail, scopes, status, createdAt, lastUsedAt})
27
+ '
28
+ ;;
29
+
30
+ import-google)
31
+ # Import from gemini-cli extension's LOCAL file (developer / on-box user).
32
+ # For remote users over Slack, use start-google-oauth + complete-google-oauth instead.
33
+ NAME=$(printf '%s' "$INPUT" | jq -r '.name // empty')
34
+ require_param "name" "$NAME"
35
+ BODY=$(jq -nc --arg name "$NAME" '{name: $name}')
36
+ RESP=$(api_call POST "/credentials/oauth/import-gemini-cli" "$BODY")
37
+ printf '%s' "$RESP" | jq '
38
+ if .success == true then
39
+ {success: true, credential: .data | {id, name, type, provider, helper, accountEmail, scopes}}
40
+ else
41
+ {success: false, error: .error}
42
+ end
43
+ '
44
+ ;;
45
+
46
+ start-google-oauth)
47
+ # Headless flow — returns a URL for a remote user to click.
48
+ # Optionally takes scopes override; defaults to broad Workspace set.
49
+ SCOPES_ARG=$(printf '%s' "$INPUT" | jq -c '.scopes // empty')
50
+ if [ -n "$SCOPES_ARG" ]; then
51
+ BODY=$(printf '%s' "$SCOPES_ARG" | jq -c '{scopes: .}')
52
+ else
53
+ BODY='{}'
54
+ fi
55
+ RESP=$(api_call POST "/credentials/oauth/google/start" "$BODY")
56
+ printf '%s' "$RESP" | jq '
57
+ if .success == true then
58
+ {
59
+ success: true,
60
+ authUrl: .data.authUrl,
61
+ instructions: "Send authUrl to the user. They click it on their device, sign in with the Google account they want to add, and copy the JSON shown on the success page. Then call action=complete-google-oauth with the pasted JSON."
62
+ }
63
+ else
64
+ {success: false, error: .error}
65
+ end
66
+ '
67
+ ;;
68
+
69
+ complete-google-oauth)
70
+ # Accept the JSON the user pasted from the success page. Save as a new credential.
71
+ NAME=$(printf '%s' "$INPUT" | jq -r '.name // empty')
72
+ require_param "name" "$NAME"
73
+ # Accept either the raw JSON string or a parsed object under .credentialsJson
74
+ CREDS=$(printf '%s' "$INPUT" | jq -c '.credentialsJson // empty')
75
+ if [ -z "$CREDS" ] || [ "$CREDS" = "null" ]; then
76
+ error_exit "credentialsJson is required (paste the JSON block from the OAuth success page)"
77
+ fi
78
+ BODY=$(jq -nc --arg name "$NAME" --argjson creds "$CREDS" '{name: $name, credentialsJson: $creds}')
79
+ RESP=$(api_call POST "/credentials/oauth/google/complete" "$BODY")
80
+ printf '%s' "$RESP" | jq '
81
+ if .success == true then
82
+ {success: true, credential: .data | {id, name, type, provider, helper, accountEmail, scopes, status}}
83
+ else
84
+ {success: false, error: .error}
85
+ end
86
+ '
87
+ ;;
88
+
89
+ clear-gemini-cli)
90
+ api_call POST "/credentials/oauth/gemini-cli/clear-extension-file" "" \
91
+ | jq '{cleared: (.success == true), error: .error}'
92
+ ;;
93
+
94
+ add-api-key)
95
+ NAME=$(printf '%s' "$INPUT" | jq -r '.name // empty')
96
+ PROVIDER=$(printf '%s' "$INPUT" | jq -r '.provider // empty')
97
+ VALUE=$(printf '%s' "$INPUT" | jq -r '.value // empty')
98
+ require_param "name" "$NAME"
99
+ require_param "provider" "$PROVIDER"
100
+ require_param "value" "$VALUE"
101
+ BODY=$(jq -nc --arg name "$NAME" --arg provider "$PROVIDER" --arg value "$VALUE" \
102
+ '{name: $name, provider: $provider, value: $value}')
103
+ RESP=$(api_call POST "/credentials/api-key" "$BODY")
104
+ printf '%s' "$RESP" | jq '
105
+ if .success == true then
106
+ {success: true, credential: .data | {id, name, type, provider, createdAt}}
107
+ else
108
+ {success: false, error: .error}
109
+ end
110
+ '
111
+ ;;
112
+
113
+ delete)
114
+ ID=$(printf '%s' "$INPUT" | jq -r '.id // empty')
115
+ require_param "id" "$ID"
116
+ api_call DELETE "/credentials/${ID}" | jq '{deleted: (.success == true), error: .error}'
117
+ ;;
118
+
119
+ read-gmail)
120
+ NAME=$(printf '%s' "$INPUT" | jq -r '.name // empty')
121
+ MAX=$(printf '%s' "$INPUT" | jq -r '.maxResults // 10')
122
+ require_param "name" "$NAME"
123
+
124
+ # 1. Find the credential by name
125
+ LIST_RESP=$(api_call GET "/credentials")
126
+ CRED_ID=$(printf '%s' "$LIST_RESP" | jq -r --arg name "$NAME" '
127
+ .data[]? | select(.name == $name and .type == "google-oauth") | .id
128
+ ' | head -n 1)
129
+
130
+ if [ -z "$CRED_ID" ]; then
131
+ AVAILABLE=$(printf '%s' "$LIST_RESP" | jq -r '
132
+ .data[]? | select(.type == "google-oauth") | .name
133
+ ' | paste -sd ", " -)
134
+ error_exit "No google-oauth credential named '$NAME'. Available Google accounts: ${AVAILABLE:-none}. Use action=list to confirm."
135
+ fi
136
+
137
+ # 2. Execute gmail-reader skill with the credential bound to 'gmail' slot
138
+ EXEC_BODY=$(jq -nc --arg cid "$CRED_ID" --arg max "$MAX" '{
139
+ agentId: "orchestrator",
140
+ roleId: "orchestrator",
141
+ userInput: $max,
142
+ credentialBindings: {gmail: $cid}
143
+ }')
144
+ RESP=$(api_call POST "/skills/skill-gmail-reader/execute" "$EXEC_BODY")
145
+ # Extract the stdout from the skill execution so the orchestrator sees
146
+ # the human-readable summary directly
147
+ printf '%s' "$RESP" | jq '
148
+ if .success == true then
149
+ {
150
+ credential: "'"$NAME"'",
151
+ credentialId: "'"$CRED_ID"'",
152
+ success: .data.success,
153
+ output: .data.output,
154
+ error: .data.error,
155
+ durationMs: .data.durationMs
156
+ }
157
+ else
158
+ {success: false, error: .error}
159
+ end
160
+ '
161
+ ;;
162
+
163
+ *)
164
+ error_exit "Unknown action: '$ACTION'. Valid: list, start-google-oauth, complete-google-oauth, import-google, clear-gemini-cli, add-api-key, delete, read-gmail"
165
+ ;;
166
+ esac
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Credentials REST Controller
3
+ *
4
+ * Exposes workspace credential management over REST so the frontend (and
5
+ * Cloud portal) can list, add, rename, delete, and OAuth-import credentials.
6
+ *
7
+ * All endpoints return `{ success, data?, error? }` matching the rest of
8
+ * the Crewly API surface. Secret values (token payloads, API keys) are
9
+ * never returned — only registry metadata.
10
+ *
11
+ * @module controllers/credentials/credentials.controller
12
+ */
13
+ import type { Request, Response, NextFunction } from 'express';
14
+ /** Reset the helper singleton — for tests only. */
15
+ export declare function _resetGeminiHelperForTesting(): void;
16
+ /**
17
+ * GET /api/credentials — list all credential metadata (never values).
18
+ */
19
+ export declare function listCredentials(_req: Request, res: Response, next: NextFunction): Promise<void>;
20
+ /**
21
+ * GET /api/credentials/:id — metadata for a single credential.
22
+ */
23
+ export declare function getCredentialById(req: Request, res: Response, next: NextFunction): Promise<void>;
24
+ /**
25
+ * POST /api/credentials/api-key — add an API key credential.
26
+ * Body: { name, provider, value }
27
+ */
28
+ export declare function addApiKey(req: Request, res: Response, next: NextFunction): Promise<void>;
29
+ /**
30
+ * PATCH /api/credentials/:id — update metadata (name, status).
31
+ * Body: { name?, status? }
32
+ */
33
+ export declare function updateCredentialHandler(req: Request, res: Response, next: NextFunction): Promise<void>;
34
+ /**
35
+ * DELETE /api/credentials/:id — delete credential (registry entry + .enc file).
36
+ */
37
+ export declare function deleteCredentialHandler(req: Request, res: Response, next: NextFunction): Promise<void>;
38
+ /**
39
+ * POST /api/credentials/oauth/import-gemini-cli — capture current extension
40
+ * login into a new credential.
41
+ * Body: { name }
42
+ *
43
+ * Precondition: the user has run
44
+ * `GEMINI_CLI_WORKSPACE_FORCE_FILE_STORAGE=true gemini`
45
+ * and signed in via the workspace extension.
46
+ */
47
+ export declare function importOAuthFromGeminiCli(req: Request, res: Response, next: NextFunction): Promise<void>;
48
+ /**
49
+ * POST /api/credentials/oauth/gemini-cli/clear-extension-file —
50
+ * delete the extension's cached token file so the next extension login
51
+ * captures a different account.
52
+ */
53
+ export declare function clearGeminiCliExtensionFile(_req: Request, res: Response, next: NextFunction): Promise<void>;
54
+ /**
55
+ * POST /api/credentials/oauth/google/start
56
+ *
57
+ * Build a Google OAuth URL that the caller can send to a remote user (via
58
+ * Slack, email, etc.). The URL opens on the user's device, they sign in,
59
+ * and the gemini-cli cloud function returns a page showing the credentials
60
+ * JSON for the user to copy back.
61
+ *
62
+ * Body: { scopes?: string[] } (optional override)
63
+ * Returns: { success, data: { authUrl } }
64
+ */
65
+ export declare function startGoogleOAuth(req: Request, res: Response, _next: NextFunction): Promise<void>;
66
+ /**
67
+ * POST /api/credentials/oauth/google/complete
68
+ *
69
+ * Accept the credentials JSON that the user copied from the OAuth success
70
+ * page, verify it, fetch the account email, and save as a new Crewly
71
+ * credential.
72
+ *
73
+ * Body: {
74
+ * name: string,
75
+ * credentialsJson: string | object (the JSON blob the user pasted)
76
+ * }
77
+ * Returns: { success, data: CredentialRegistryEntry }
78
+ */
79
+ export declare function completeGoogleOAuth(req: Request, res: Response, _next: NextFunction): Promise<void>;
80
+ //# sourceMappingURL=credentials.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/credentials/credentials.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA6B/D,mDAAmD;AACnD,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAMD;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAQf;AAoCD;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAmGf"}