@hera-al/server 1.6.12 → 1.6.13

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 (61) hide show
  1. package/bundled/a2ui/SKILL.md +339 -0
  2. package/bundled/buongiorno/SKILL.md +151 -0
  3. package/bundled/council/SKILL.md +168 -0
  4. package/bundled/council/scripts/council.mjs +202 -0
  5. package/bundled/dreaming/SKILL.md +177 -0
  6. package/bundled/google-workspace/SKILL.md +229 -0
  7. package/bundled/google-workspace/scripts/auth.sh +87 -0
  8. package/bundled/google-workspace/scripts/calendar.sh +508 -0
  9. package/bundled/google-workspace/scripts/drive.sh +459 -0
  10. package/bundled/google-workspace/scripts/gmail.sh +452 -0
  11. package/bundled/humanizer/SKILL.md +488 -0
  12. package/bundled/librarian/SKILL.md +155 -0
  13. package/bundled/plasma/SKILL.md +1417 -0
  14. package/bundled/sera/SKILL.md +143 -0
  15. package/bundled/the-skill-guardian/SKILL.md +103 -0
  16. package/bundled/the-skill-guardian/scripts/scan.sh +314 -0
  17. package/bundled/unix-time/SKILL.md +58 -0
  18. package/bundled/wandering/SKILL.md +174 -0
  19. package/bundled/xai-search/SKILL.md +91 -0
  20. package/bundled/xai-search/scripts/search.sh +197 -0
  21. package/dist/a2ui/parser.d.ts +76 -0
  22. package/dist/a2ui/parser.js +1 -0
  23. package/dist/a2ui/types.d.ts +147 -0
  24. package/dist/a2ui/types.js +1 -0
  25. package/dist/a2ui/validator.d.ts +32 -0
  26. package/dist/a2ui/validator.js +1 -0
  27. package/dist/agent/agent-service.d.ts +17 -11
  28. package/dist/agent/agent-service.js +1 -1
  29. package/dist/agent/session-agent.d.ts +1 -1
  30. package/dist/agent/session-agent.js +1 -1
  31. package/dist/agent/session-error-handler.js +1 -1
  32. package/dist/commands/debuga2ui.d.ts +13 -0
  33. package/dist/commands/debuga2ui.js +1 -0
  34. package/dist/commands/debugdynamic.d.ts +13 -0
  35. package/dist/commands/debugdynamic.js +1 -0
  36. package/dist/commands/mcp.d.ts +6 -3
  37. package/dist/commands/mcp.js +1 -1
  38. package/dist/gateway/node-registry.d.ts +29 -1
  39. package/dist/gateway/node-registry.js +1 -1
  40. package/dist/installer/hera.js +1 -1
  41. package/dist/memory/concept-store.d.ts +109 -0
  42. package/dist/memory/concept-store.js +1 -0
  43. package/dist/nostromo/nostromo.js +1 -1
  44. package/dist/server.d.ts +3 -2
  45. package/dist/server.js +1 -1
  46. package/dist/tools/a2ui-tools.d.ts +23 -0
  47. package/dist/tools/a2ui-tools.js +1 -0
  48. package/dist/tools/concept-tools.d.ts +3 -0
  49. package/dist/tools/concept-tools.js +1 -0
  50. package/dist/tools/dynamic-ui-tools.d.ts +25 -0
  51. package/dist/tools/dynamic-ui-tools.js +1 -0
  52. package/dist/tools/node-tools.js +1 -1
  53. package/dist/tools/plasma-client-tools.d.ts +28 -0
  54. package/dist/tools/plasma-client-tools.js +1 -0
  55. package/installationPkg/AGENTS.md +168 -22
  56. package/installationPkg/SOUL.md +56 -0
  57. package/installationPkg/TOOLS.md +126 -0
  58. package/installationPkg/USER.md +54 -1
  59. package/installationPkg/config.example.yaml +145 -34
  60. package/installationPkg/default-jobs.json +77 -0
  61. package/package.json +3 -2
@@ -0,0 +1,452 @@
1
+ #!/usr/bin/env bash
2
+ # Gmail API client — OAuth2 REST API v1
3
+ # Usage: gmail.sh <command> [options]
4
+ #
5
+ # Commands:
6
+ # list [--max N] List recent messages
7
+ # search "query" [--max N] Search messages (Gmail query syntax)
8
+ # read <messageId> [--format minimal|full] Read a message
9
+ # send --to ADDR --subject SUBJ --body TXT Send an email
10
+ # reply <messageId> --body TXT Reply to an email
11
+ # labels List all labels
12
+ # label <messageId> --add L --remove L Modify labels
13
+ # mark-read <messageId> Mark as read
14
+ # mark-unread <messageId> Mark as unread
15
+ # trash <messageId> Move to trash
16
+ # threads [--max N] List threads
17
+ # thread <threadId> Get thread
18
+
19
+ set -euo pipefail
20
+
21
+ # --- Config ---
22
+ API_BASE="https://gmail.googleapis.com/gmail/v1/users/me"
23
+ TOKEN_FILE="${HOME}/.gmab-google-token.json"
24
+
25
+ # --- Token management ---
26
+ get_access_token() {
27
+ local client_id="${GOOGLE_CLIENT_ID:-}"
28
+ local client_secret="${GOOGLE_CLIENT_SECRET:-}"
29
+ local refresh_token="${GOOGLE_REFRESH_TOKEN:-}"
30
+
31
+ # Try token file if env vars not set
32
+ if [[ -z "$refresh_token" && -f "$TOKEN_FILE" ]]; then
33
+ client_id=$(jq -r '.client_id // empty' "$TOKEN_FILE" 2>/dev/null || true)
34
+ client_secret=$(jq -r '.client_secret // empty' "$TOKEN_FILE" 2>/dev/null || true)
35
+ refresh_token=$(jq -r '.refresh_token // empty' "$TOKEN_FILE" 2>/dev/null || true)
36
+ fi
37
+
38
+ if [[ -z "$client_id" || -z "$client_secret" || -z "$refresh_token" ]]; then
39
+ echo "ERROR: Google credentials not configured." >&2
40
+ echo "Set GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN" >&2
41
+ echo "Or run scripts/google/auth.sh to set up OAuth2." >&2
42
+ exit 1
43
+ fi
44
+
45
+ # Check cached token
46
+ if [[ -f "$TOKEN_FILE" ]]; then
47
+ local cached_token cached_expiry
48
+ cached_token=$(jq -r '.access_token // empty' "$TOKEN_FILE" 2>/dev/null || true)
49
+ cached_expiry=$(jq -r '.expires_at // 0' "$TOKEN_FILE" 2>/dev/null || true)
50
+ local now
51
+ now=$(date +%s)
52
+ if [[ -n "$cached_token" && "$cached_expiry" -gt "$now" ]]; then
53
+ echo "$cached_token"
54
+ return
55
+ fi
56
+ fi
57
+
58
+ # Refresh the token
59
+ local response
60
+ response=$(curl -s "https://oauth2.googleapis.com/token" \
61
+ -d "client_id=$client_id" \
62
+ -d "client_secret=$client_secret" \
63
+ -d "refresh_token=$refresh_token" \
64
+ -d "grant_type=refresh_token")
65
+
66
+ local access_token
67
+ access_token=$(echo "$response" | jq -r '.access_token // empty')
68
+ local expires_in
69
+ expires_in=$(echo "$response" | jq -r '.expires_in // 3600')
70
+
71
+ if [[ -z "$access_token" ]]; then
72
+ echo "ERROR: Failed to refresh access token" >&2
73
+ echo "$response" >&2
74
+ exit 1
75
+ fi
76
+
77
+ # Cache token
78
+ local expires_at
79
+ expires_at=$(($(date +%s) + expires_in - 60))
80
+
81
+ # Preserve existing fields, update token
82
+ if [[ -f "$TOKEN_FILE" ]]; then
83
+ local tmp
84
+ tmp=$(jq --arg at "$access_token" --argjson ea "$expires_at" \
85
+ '. + {access_token: $at, expires_at: $ea}' "$TOKEN_FILE")
86
+ echo "$tmp" > "$TOKEN_FILE"
87
+ else
88
+ jq -n \
89
+ --arg ci "$client_id" \
90
+ --arg cs "$client_secret" \
91
+ --arg rt "$refresh_token" \
92
+ --arg at "$access_token" \
93
+ --argjson ea "$expires_at" \
94
+ '{client_id: $ci, client_secret: $cs, refresh_token: $rt, access_token: $at, expires_at: $ea}' \
95
+ > "$TOKEN_FILE"
96
+ fi
97
+ chmod 600 "$TOKEN_FILE"
98
+
99
+ echo "$access_token"
100
+ }
101
+
102
+ api_get() {
103
+ local path="$1"
104
+ local token
105
+ token=$(get_access_token)
106
+ curl -s -H "Authorization: Bearer $token" "${API_BASE}${path}"
107
+ }
108
+
109
+ api_post() {
110
+ local path="$1"
111
+ local data="${2:-}"
112
+ local token
113
+ token=$(get_access_token)
114
+ if [[ -n "$data" ]]; then
115
+ curl -s -H "Authorization: Bearer $token" \
116
+ -H "Content-Type: application/json" \
117
+ -d "$data" "${API_BASE}${path}"
118
+ else
119
+ curl -s -X POST -H "Authorization: Bearer $token" "${API_BASE}${path}"
120
+ fi
121
+ }
122
+
123
+ # --- Helpers ---
124
+ format_message_list() {
125
+ jq -r '
126
+ .messages[]? | "\(.id)\t\(.threadId)"
127
+ '
128
+ }
129
+
130
+ format_message() {
131
+ local format="${1:-metadata}"
132
+ if [[ "$format" == "minimal" ]]; then
133
+ jq -r '{id, threadId, labelIds, snippet}'
134
+ else
135
+ jq -r '
136
+ def header(name): .payload.headers[]? | select(.name == name) | .value;
137
+ {
138
+ id: .id,
139
+ threadId: .threadId,
140
+ labels: .labelIds,
141
+ from: header("From"),
142
+ to: header("To"),
143
+ cc: (header("Cc") // null),
144
+ subject: header("Subject"),
145
+ date: header("Date"),
146
+ snippet: .snippet,
147
+ body: (
148
+ # Try to get text/plain body
149
+ if .payload.body.data then
150
+ .payload.body.data
151
+ elif .payload.parts then
152
+ (.payload.parts[]? | select(.mimeType == "text/plain") | .body.data) //
153
+ (.payload.parts[]? | select(.mimeType == "text/html") | .body.data)
154
+ else
155
+ null
156
+ end
157
+ )
158
+ }
159
+ '
160
+ fi
161
+ }
162
+
163
+ decode_base64url() {
164
+ # Gmail uses URL-safe base64 — convert to standard base64 then decode
165
+ tr '_-' '/+' | base64 -d 2>/dev/null || echo "[decode error]"
166
+ }
167
+
168
+ # --- Commands ---
169
+ cmd_list() {
170
+ local max=10
171
+ while [[ $# -gt 0 ]]; do
172
+ case "$1" in
173
+ --max) max="$2"; shift 2 ;;
174
+ *) shift ;;
175
+ esac
176
+ done
177
+
178
+ local response
179
+ response=$(api_get "/messages?maxResults=$max&labelIds=INBOX")
180
+
181
+ # Get message IDs, then fetch metadata for each
182
+ local ids
183
+ ids=$(echo "$response" | jq -r '.messages[]?.id // empty')
184
+
185
+ if [[ -z "$ids" ]]; then
186
+ echo "No messages found."
187
+ return
188
+ fi
189
+
190
+ echo "["
191
+ local first=true
192
+ while IFS= read -r mid; do
193
+ [[ -z "$mid" ]] && continue
194
+ if [[ "$first" == "true" ]]; then
195
+ first=false
196
+ else
197
+ echo ","
198
+ fi
199
+ api_get "/messages/$mid?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date" | jq '{
200
+ id: .id,
201
+ threadId: .threadId,
202
+ labels: .labelIds,
203
+ snippet: .snippet,
204
+ from: (.payload.headers[]? | select(.name == "From") | .value),
205
+ subject: (.payload.headers[]? | select(.name == "Subject") | .value),
206
+ date: (.payload.headers[]? | select(.name == "Date") | .value)
207
+ }'
208
+ done <<< "$ids"
209
+ echo "]"
210
+ }
211
+
212
+ cmd_search() {
213
+ local query="$1"; shift
214
+ local max=10
215
+ while [[ $# -gt 0 ]]; do
216
+ case "$1" in
217
+ --max) max="$2"; shift 2 ;;
218
+ *) shift ;;
219
+ esac
220
+ done
221
+
222
+ local encoded_query
223
+ encoded_query=$(python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))" <<< "$query" 2>/dev/null || echo "$query")
224
+
225
+ local response
226
+ response=$(api_get "/messages?maxResults=$max&q=$encoded_query")
227
+
228
+ local ids
229
+ ids=$(echo "$response" | jq -r '.messages[]?.id // empty')
230
+
231
+ if [[ -z "$ids" ]]; then
232
+ echo "No messages found for query: $query"
233
+ return
234
+ fi
235
+
236
+ echo "["
237
+ local first=true
238
+ while IFS= read -r mid; do
239
+ [[ -z "$mid" ]] && continue
240
+ if [[ "$first" == "true" ]]; then
241
+ first=false
242
+ else
243
+ echo ","
244
+ fi
245
+ api_get "/messages/$mid?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date" | jq '{
246
+ id: .id,
247
+ threadId: .threadId,
248
+ labels: .labelIds,
249
+ snippet: .snippet,
250
+ from: (.payload.headers[]? | select(.name == "From") | .value),
251
+ subject: (.payload.headers[]? | select(.name == "Subject") | .value),
252
+ date: (.payload.headers[]? | select(.name == "Date") | .value)
253
+ }'
254
+ done <<< "$ids"
255
+ echo "]"
256
+ }
257
+
258
+ cmd_read() {
259
+ local msg_id="$1"; shift
260
+ local format="full"
261
+ while [[ $# -gt 0 ]]; do
262
+ case "$1" in
263
+ --format) format="$2"; shift 2 ;;
264
+ *) shift ;;
265
+ esac
266
+ done
267
+
268
+ local response
269
+ response=$(api_get "/messages/$msg_id?format=$format")
270
+
271
+ if [[ "$format" == "minimal" ]]; then
272
+ echo "$response" | format_message "minimal"
273
+ else
274
+ # Extract and decode body
275
+ local parsed
276
+ parsed=$(echo "$response" | format_message "full")
277
+ local body_b64
278
+ body_b64=$(echo "$parsed" | jq -r '.body // empty')
279
+
280
+ if [[ -n "$body_b64" ]]; then
281
+ local decoded
282
+ decoded=$(echo "$body_b64" | decode_base64url)
283
+ echo "$parsed" | jq --arg body "$decoded" '.body = $body'
284
+ else
285
+ echo "$parsed" | jq '.body = "[no text body found]"'
286
+ fi
287
+ fi
288
+ }
289
+
290
+ cmd_send() {
291
+ local to="" subject="" body="" cc="" bcc=""
292
+ while [[ $# -gt 0 ]]; do
293
+ case "$1" in
294
+ --to) to="$2"; shift 2 ;;
295
+ --subject) subject="$2"; shift 2 ;;
296
+ --body) body="$2"; shift 2 ;;
297
+ --cc) cc="$2"; shift 2 ;;
298
+ --bcc) bcc="$2"; shift 2 ;;
299
+ *) shift ;;
300
+ esac
301
+ done
302
+
303
+ if [[ -z "$to" || -z "$subject" ]]; then
304
+ echo "ERROR: --to and --subject are required" >&2
305
+ exit 1
306
+ fi
307
+
308
+ # Build RFC 2822 message
309
+ local raw_msg="To: $to\nSubject: $subject\nContent-Type: text/plain; charset=utf-8\n"
310
+ [[ -n "$cc" ]] && raw_msg+="Cc: $cc\n"
311
+ [[ -n "$bcc" ]] && raw_msg+="Bcc: $bcc\n"
312
+ raw_msg+="\n$body"
313
+
314
+ # Base64url encode
315
+ local encoded
316
+ encoded=$(printf '%b' "$raw_msg" | base64 | tr '+/' '-_' | tr -d '=\n')
317
+
318
+ local data
319
+ data=$(jq -n --arg raw "$encoded" '{"raw": $raw}')
320
+
321
+ api_post "/messages/send" "$data" | jq '{id: .id, threadId: .threadId, labelIds: .labelIds}'
322
+ }
323
+
324
+ cmd_reply() {
325
+ local msg_id="$1"; shift
326
+ local body=""
327
+ while [[ $# -gt 0 ]]; do
328
+ case "$1" in
329
+ --body) body="$2"; shift 2 ;;
330
+ *) shift ;;
331
+ esac
332
+ done
333
+
334
+ if [[ -z "$body" ]]; then
335
+ echo "ERROR: --body is required" >&2
336
+ exit 1
337
+ fi
338
+
339
+ # Get original message to extract headers
340
+ local original
341
+ original=$(api_get "/messages/$msg_id?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Message-Id&metadataHeaders=To")
342
+
343
+ local from subject message_id thread_id
344
+ from=$(echo "$original" | jq -r '.payload.headers[]? | select(.name == "From") | .value')
345
+ subject=$(echo "$original" | jq -r '.payload.headers[]? | select(.name == "Subject") | .value')
346
+ message_id=$(echo "$original" | jq -r '.payload.headers[]? | select(.name == "Message-Id") | .value')
347
+ thread_id=$(echo "$original" | jq -r '.threadId')
348
+
349
+ # Add Re: prefix if not already present
350
+ [[ "$subject" != Re:* ]] && subject="Re: $subject"
351
+
352
+ local raw_msg="To: $from\nSubject: $subject\nIn-Reply-To: $message_id\nReferences: $message_id\nContent-Type: text/plain; charset=utf-8\n\n$body"
353
+
354
+ local encoded
355
+ encoded=$(printf '%b' "$raw_msg" | base64 | tr '+/' '-_' | tr -d '=\n')
356
+
357
+ local data
358
+ data=$(jq -n --arg raw "$encoded" --arg tid "$thread_id" '{"raw": $raw, "threadId": $tid}')
359
+
360
+ api_post "/messages/send" "$data" | jq '{id: .id, threadId: .threadId}'
361
+ }
362
+
363
+ cmd_labels() {
364
+ api_get "/labels" | jq '[.labels[]? | {id: .id, name: .name, type: .type}]'
365
+ }
366
+
367
+ cmd_label() {
368
+ local msg_id="$1"; shift
369
+ local add_labels=() remove_labels=()
370
+ while [[ $# -gt 0 ]]; do
371
+ case "$1" in
372
+ --add) add_labels+=("$2"); shift 2 ;;
373
+ --remove) remove_labels+=("$2"); shift 2 ;;
374
+ *) shift ;;
375
+ esac
376
+ done
377
+
378
+ local data
379
+ data=$(jq -n \
380
+ --argjson add "$(printf '%s\n' "${add_labels[@]}" | jq -R . | jq -s .)" \
381
+ --argjson remove "$(printf '%s\n' "${remove_labels[@]}" | jq -R . | jq -s .)" \
382
+ '{addLabelIds: $add, removeLabelIds: $remove}')
383
+
384
+ api_post "/messages/$msg_id/modify" "$data" | jq '{id: .id, labelIds: .labelIds}'
385
+ }
386
+
387
+ cmd_mark_read() {
388
+ local msg_id="$1"
389
+ api_post "/messages/$msg_id/modify" '{"removeLabelIds": ["UNREAD"]}' | jq '{id: .id, labelIds: .labelIds}'
390
+ }
391
+
392
+ cmd_mark_unread() {
393
+ local msg_id="$1"
394
+ api_post "/messages/$msg_id/modify" '{"addLabelIds": ["UNREAD"]}' | jq '{id: .id, labelIds: .labelIds}'
395
+ }
396
+
397
+ cmd_trash() {
398
+ local msg_id="$1"
399
+ api_post "/messages/$msg_id/trash" | jq '{id: .id, labelIds: .labelIds}'
400
+ }
401
+
402
+ cmd_threads() {
403
+ local max=10
404
+ while [[ $# -gt 0 ]]; do
405
+ case "$1" in
406
+ --max) max="$2"; shift 2 ;;
407
+ *) shift ;;
408
+ esac
409
+ done
410
+ api_get "/threads?maxResults=$max" | jq '[.threads[]? | {id: .id, snippet: .snippet, historyId: .historyId}]'
411
+ }
412
+
413
+ cmd_thread() {
414
+ local thread_id="$1"
415
+ api_get "/threads/$thread_id?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date" | jq '{
416
+ id: .id,
417
+ messages: [.messages[]? | {
418
+ id: .id,
419
+ labels: .labelIds,
420
+ snippet: .snippet,
421
+ from: (.payload.headers[]? | select(.name == "From") | .value),
422
+ subject: (.payload.headers[]? | select(.name == "Subject") | .value),
423
+ date: (.payload.headers[]? | select(.name == "Date") | .value)
424
+ }]
425
+ }'
426
+ }
427
+
428
+ # --- Dispatch ---
429
+ COMMAND="${1:-}"
430
+ shift || true
431
+
432
+ case "$COMMAND" in
433
+ list) cmd_list "$@" ;;
434
+ search) cmd_search "$@" ;;
435
+ read) cmd_read "$@" ;;
436
+ send) cmd_send "$@" ;;
437
+ reply) cmd_reply "$@" ;;
438
+ labels) cmd_labels ;;
439
+ label) cmd_label "$@" ;;
440
+ mark-read) cmd_mark_read "$@" ;;
441
+ mark-unread) cmd_mark_unread "$@" ;;
442
+ trash) cmd_trash "$@" ;;
443
+ threads) cmd_threads "$@" ;;
444
+ thread) cmd_thread "$@" ;;
445
+ *)
446
+ echo "Usage: gmail.sh <command> [options]" >&2
447
+ echo "" >&2
448
+ echo "Commands: list, search, read, send, reply, labels, label," >&2
449
+ echo " mark-read, mark-unread, trash, threads, thread" >&2
450
+ exit 1
451
+ ;;
452
+ esac