@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.
- package/bundled/a2ui/SKILL.md +339 -0
- package/bundled/buongiorno/SKILL.md +151 -0
- package/bundled/council/SKILL.md +168 -0
- package/bundled/council/scripts/council.mjs +202 -0
- package/bundled/dreaming/SKILL.md +177 -0
- package/bundled/google-workspace/SKILL.md +229 -0
- package/bundled/google-workspace/scripts/auth.sh +87 -0
- package/bundled/google-workspace/scripts/calendar.sh +508 -0
- package/bundled/google-workspace/scripts/drive.sh +459 -0
- package/bundled/google-workspace/scripts/gmail.sh +452 -0
- package/bundled/humanizer/SKILL.md +488 -0
- package/bundled/librarian/SKILL.md +155 -0
- package/bundled/plasma/SKILL.md +1417 -0
- package/bundled/sera/SKILL.md +143 -0
- package/bundled/the-skill-guardian/SKILL.md +103 -0
- package/bundled/the-skill-guardian/scripts/scan.sh +314 -0
- package/bundled/unix-time/SKILL.md +58 -0
- package/bundled/wandering/SKILL.md +174 -0
- package/bundled/xai-search/SKILL.md +91 -0
- package/bundled/xai-search/scripts/search.sh +197 -0
- package/dist/a2ui/parser.d.ts +76 -0
- package/dist/a2ui/parser.js +1 -0
- package/dist/a2ui/types.d.ts +147 -0
- package/dist/a2ui/types.js +1 -0
- package/dist/a2ui/validator.d.ts +32 -0
- package/dist/a2ui/validator.js +1 -0
- package/dist/agent/agent-service.d.ts +17 -11
- package/dist/agent/agent-service.js +1 -1
- package/dist/agent/session-agent.d.ts +1 -1
- package/dist/agent/session-agent.js +1 -1
- package/dist/agent/session-error-handler.js +1 -1
- package/dist/commands/debuga2ui.d.ts +13 -0
- package/dist/commands/debuga2ui.js +1 -0
- package/dist/commands/debugdynamic.d.ts +13 -0
- package/dist/commands/debugdynamic.js +1 -0
- package/dist/commands/mcp.d.ts +6 -3
- package/dist/commands/mcp.js +1 -1
- package/dist/gateway/node-registry.d.ts +29 -1
- package/dist/gateway/node-registry.js +1 -1
- package/dist/installer/hera.js +1 -1
- package/dist/memory/concept-store.d.ts +109 -0
- package/dist/memory/concept-store.js +1 -0
- package/dist/nostromo/nostromo.js +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.js +1 -1
- package/dist/tools/a2ui-tools.d.ts +23 -0
- package/dist/tools/a2ui-tools.js +1 -0
- package/dist/tools/concept-tools.d.ts +3 -0
- package/dist/tools/concept-tools.js +1 -0
- package/dist/tools/dynamic-ui-tools.d.ts +25 -0
- package/dist/tools/dynamic-ui-tools.js +1 -0
- package/dist/tools/node-tools.js +1 -1
- package/dist/tools/plasma-client-tools.d.ts +28 -0
- package/dist/tools/plasma-client-tools.js +1 -0
- package/installationPkg/AGENTS.md +168 -22
- package/installationPkg/SOUL.md +56 -0
- package/installationPkg/TOOLS.md +126 -0
- package/installationPkg/USER.md +54 -1
- package/installationPkg/config.example.yaml +145 -34
- package/installationPkg/default-jobs.json +77 -0
- package/package.json +3 -2
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Google Drive API client — REST API v3 with OAuth2
|
|
3
|
+
# Usage: drive.sh <command> [options]
|
|
4
|
+
#
|
|
5
|
+
# Commands:
|
|
6
|
+
# list [--max N] [--folder ID] List files
|
|
7
|
+
# search "query" [--max N] Search files (Drive query syntax)
|
|
8
|
+
# info <fileId> Get file metadata
|
|
9
|
+
# read <fileId> Read/export file content (text)
|
|
10
|
+
# download <fileId> --output PATH Download binary file
|
|
11
|
+
# upload PATH [--folder ID] [--name N] Upload a file
|
|
12
|
+
# create-doc --name N [--content T] Create a Google Doc
|
|
13
|
+
# move <fileId> --to FOLDER_ID Move file to folder
|
|
14
|
+
# rename <fileId> --name NEW_NAME Rename a file
|
|
15
|
+
# share <fileId> [options] Share a file
|
|
16
|
+
# trash <fileId> Move to trash
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# --- Config ---
|
|
21
|
+
API_BASE="https://www.googleapis.com/drive/v3"
|
|
22
|
+
UPLOAD_BASE="https://www.googleapis.com/upload/drive/v3"
|
|
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
|
+
if [[ -f "$TOKEN_FILE" ]]; then
|
|
82
|
+
local tmp
|
|
83
|
+
tmp=$(jq --arg at "$access_token" --argjson ea "$expires_at" \
|
|
84
|
+
'. + {access_token: $at, expires_at: $ea}' "$TOKEN_FILE")
|
|
85
|
+
echo "$tmp" > "$TOKEN_FILE"
|
|
86
|
+
else
|
|
87
|
+
jq -n \
|
|
88
|
+
--arg ci "$client_id" \
|
|
89
|
+
--arg cs "$client_secret" \
|
|
90
|
+
--arg rt "$refresh_token" \
|
|
91
|
+
--arg at "$access_token" \
|
|
92
|
+
--argjson ea "$expires_at" \
|
|
93
|
+
'{client_id: $ci, client_secret: $cs, refresh_token: $rt, access_token: $at, expires_at: $ea}' \
|
|
94
|
+
> "$TOKEN_FILE"
|
|
95
|
+
fi
|
|
96
|
+
chmod 600 "$TOKEN_FILE"
|
|
97
|
+
|
|
98
|
+
echo "$access_token"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
api_get() {
|
|
102
|
+
local url="$1"
|
|
103
|
+
local token
|
|
104
|
+
token=$(get_access_token)
|
|
105
|
+
curl -s -H "Authorization: Bearer $token" "$url"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
api_post() {
|
|
109
|
+
local url="$1"
|
|
110
|
+
local data="${2:-}"
|
|
111
|
+
local token
|
|
112
|
+
token=$(get_access_token)
|
|
113
|
+
if [[ -n "$data" ]]; then
|
|
114
|
+
curl -s -H "Authorization: Bearer $token" \
|
|
115
|
+
-H "Content-Type: application/json" \
|
|
116
|
+
-d "$data" "$url"
|
|
117
|
+
else
|
|
118
|
+
curl -s -X POST -H "Authorization: Bearer $token" "$url"
|
|
119
|
+
fi
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
api_patch() {
|
|
123
|
+
local url="$1"
|
|
124
|
+
local data="$2"
|
|
125
|
+
local token
|
|
126
|
+
token=$(get_access_token)
|
|
127
|
+
curl -s -X PATCH -H "Authorization: Bearer $token" \
|
|
128
|
+
-H "Content-Type: application/json" \
|
|
129
|
+
-d "$data" "$url"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
api_download() {
|
|
133
|
+
local url="$1"
|
|
134
|
+
local output="$2"
|
|
135
|
+
local token
|
|
136
|
+
token=$(get_access_token)
|
|
137
|
+
curl -s -H "Authorization: Bearer $token" -o "$output" "$url"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
urlencode() {
|
|
141
|
+
python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))" <<< "$1" 2>/dev/null || echo "$1"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# --- Commands ---
|
|
145
|
+
cmd_list() {
|
|
146
|
+
local max=20 folder=""
|
|
147
|
+
while [[ $# -gt 0 ]]; do
|
|
148
|
+
case "$1" in
|
|
149
|
+
--max) max="$2"; shift 2 ;;
|
|
150
|
+
--folder) folder="$2"; shift 2 ;;
|
|
151
|
+
*) shift ;;
|
|
152
|
+
esac
|
|
153
|
+
done
|
|
154
|
+
|
|
155
|
+
local query="trashed=false"
|
|
156
|
+
if [[ -n "$folder" ]]; then
|
|
157
|
+
query="'${folder}' in parents and trashed=false"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
local encoded_q
|
|
161
|
+
encoded_q=$(urlencode "$query")
|
|
162
|
+
|
|
163
|
+
api_get "${API_BASE}/files?pageSize=${max}&q=${encoded_q}&fields=files(id,name,mimeType,modifiedTime,size,parents)&orderBy=modifiedTime%20desc" | \
|
|
164
|
+
jq '[.files[]? | {id, name, mimeType, modifiedTime, size, parents}]'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cmd_search() {
|
|
168
|
+
local query="$1"; shift
|
|
169
|
+
local max=20
|
|
170
|
+
while [[ $# -gt 0 ]]; do
|
|
171
|
+
case "$1" in
|
|
172
|
+
--max) max="$2"; shift 2 ;;
|
|
173
|
+
*) shift ;;
|
|
174
|
+
esac
|
|
175
|
+
done
|
|
176
|
+
|
|
177
|
+
local full_query="${query} and trashed=false"
|
|
178
|
+
local encoded_q
|
|
179
|
+
encoded_q=$(urlencode "$full_query")
|
|
180
|
+
|
|
181
|
+
api_get "${API_BASE}/files?pageSize=${max}&q=${encoded_q}&fields=files(id,name,mimeType,modifiedTime,size,parents)&orderBy=modifiedTime%20desc" | \
|
|
182
|
+
jq '[.files[]? | {id, name, mimeType, modifiedTime, size, parents}]'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
cmd_info() {
|
|
186
|
+
local file_id="$1"
|
|
187
|
+
api_get "${API_BASE}/files/${file_id}?fields=id,name,mimeType,modifiedTime,createdTime,size,parents,webViewLink,webContentLink,shared,owners,permissions" | jq .
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
cmd_read() {
|
|
191
|
+
local file_id="$1"
|
|
192
|
+
|
|
193
|
+
# First get the mime type to determine export format
|
|
194
|
+
local info
|
|
195
|
+
info=$(api_get "${API_BASE}/files/${file_id}?fields=mimeType,name")
|
|
196
|
+
local mime
|
|
197
|
+
mime=$(echo "$info" | jq -r '.mimeType')
|
|
198
|
+
local name
|
|
199
|
+
name=$(echo "$info" | jq -r '.name')
|
|
200
|
+
|
|
201
|
+
echo "# File: $name ($mime)" >&2
|
|
202
|
+
|
|
203
|
+
case "$mime" in
|
|
204
|
+
application/vnd.google-apps.document)
|
|
205
|
+
api_get "${API_BASE}/files/${file_id}/export?mimeType=text/plain"
|
|
206
|
+
;;
|
|
207
|
+
application/vnd.google-apps.spreadsheet)
|
|
208
|
+
api_get "${API_BASE}/files/${file_id}/export?mimeType=text/csv"
|
|
209
|
+
;;
|
|
210
|
+
application/vnd.google-apps.presentation)
|
|
211
|
+
api_get "${API_BASE}/files/${file_id}/export?mimeType=text/plain"
|
|
212
|
+
;;
|
|
213
|
+
text/*|application/json|application/xml|application/javascript)
|
|
214
|
+
api_get "${API_BASE}/files/${file_id}?alt=media"
|
|
215
|
+
;;
|
|
216
|
+
*)
|
|
217
|
+
echo "Binary file — use 'download' command instead." >&2
|
|
218
|
+
echo "MIME type: $mime"
|
|
219
|
+
;;
|
|
220
|
+
esac
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
cmd_download() {
|
|
224
|
+
local file_id="$1"; shift
|
|
225
|
+
local output=""
|
|
226
|
+
while [[ $# -gt 0 ]]; do
|
|
227
|
+
case "$1" in
|
|
228
|
+
--output|-o) output="$2"; shift 2 ;;
|
|
229
|
+
*) shift ;;
|
|
230
|
+
esac
|
|
231
|
+
done
|
|
232
|
+
|
|
233
|
+
if [[ -z "$output" ]]; then
|
|
234
|
+
# Auto-name from file metadata
|
|
235
|
+
local info
|
|
236
|
+
info=$(api_get "${API_BASE}/files/${file_id}?fields=name")
|
|
237
|
+
output=$(echo "$info" | jq -r '.name')
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
# Check if it's a Google Workspace file (needs export)
|
|
241
|
+
local info
|
|
242
|
+
info=$(api_get "${API_BASE}/files/${file_id}?fields=mimeType")
|
|
243
|
+
local mime
|
|
244
|
+
mime=$(echo "$info" | jq -r '.mimeType')
|
|
245
|
+
|
|
246
|
+
case "$mime" in
|
|
247
|
+
application/vnd.google-apps.document)
|
|
248
|
+
api_download "${API_BASE}/files/${file_id}/export?mimeType=application/pdf" "$output"
|
|
249
|
+
;;
|
|
250
|
+
application/vnd.google-apps.spreadsheet)
|
|
251
|
+
api_download "${API_BASE}/files/${file_id}/export?mimeType=application/pdf" "$output"
|
|
252
|
+
;;
|
|
253
|
+
application/vnd.google-apps.presentation)
|
|
254
|
+
api_download "${API_BASE}/files/${file_id}/export?mimeType=application/pdf" "$output"
|
|
255
|
+
;;
|
|
256
|
+
*)
|
|
257
|
+
api_download "${API_BASE}/files/${file_id}?alt=media" "$output"
|
|
258
|
+
;;
|
|
259
|
+
esac
|
|
260
|
+
|
|
261
|
+
echo "Downloaded to: $output"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
cmd_upload() {
|
|
265
|
+
local file_path="$1"; shift
|
|
266
|
+
local folder="" name=""
|
|
267
|
+
while [[ $# -gt 0 ]]; do
|
|
268
|
+
case "$1" in
|
|
269
|
+
--folder) folder="$2"; shift 2 ;;
|
|
270
|
+
--name) name="$2"; shift 2 ;;
|
|
271
|
+
*) shift ;;
|
|
272
|
+
esac
|
|
273
|
+
done
|
|
274
|
+
|
|
275
|
+
if [[ ! -f "$file_path" ]]; then
|
|
276
|
+
echo "ERROR: File not found: $file_path" >&2
|
|
277
|
+
exit 1
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
[[ -z "$name" ]] && name=$(basename "$file_path")
|
|
281
|
+
|
|
282
|
+
# Detect MIME type
|
|
283
|
+
local mime
|
|
284
|
+
mime=$(file --mime-type -b "$file_path")
|
|
285
|
+
|
|
286
|
+
# Build metadata
|
|
287
|
+
local metadata
|
|
288
|
+
if [[ -n "$folder" ]]; then
|
|
289
|
+
metadata=$(jq -n --arg name "$name" --arg folder "$folder" '{name: $name, parents: [$folder]}')
|
|
290
|
+
else
|
|
291
|
+
metadata=$(jq -n --arg name "$name" '{name: $name}')
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
local token
|
|
295
|
+
token=$(get_access_token)
|
|
296
|
+
|
|
297
|
+
# Multipart upload
|
|
298
|
+
local boundary="gmab_upload_boundary"
|
|
299
|
+
local tmpfile
|
|
300
|
+
tmpfile=$(mktemp)
|
|
301
|
+
|
|
302
|
+
{
|
|
303
|
+
printf -- "--%s\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n%s\r\n" "$boundary" "$metadata"
|
|
304
|
+
printf -- "--%s\r\nContent-Type: %s\r\n\r\n" "$boundary" "$mime"
|
|
305
|
+
cat "$file_path"
|
|
306
|
+
printf "\r\n--%s--\r\n" "$boundary"
|
|
307
|
+
} > "$tmpfile"
|
|
308
|
+
|
|
309
|
+
curl -s -X POST \
|
|
310
|
+
-H "Authorization: Bearer $token" \
|
|
311
|
+
-H "Content-Type: multipart/related; boundary=$boundary" \
|
|
312
|
+
--data-binary "@$tmpfile" \
|
|
313
|
+
"${UPLOAD_BASE}/files?uploadType=multipart&fields=id,name,mimeType,webViewLink" | jq .
|
|
314
|
+
|
|
315
|
+
rm -f "$tmpfile"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
cmd_create_doc() {
|
|
319
|
+
local name="" content="" folder=""
|
|
320
|
+
while [[ $# -gt 0 ]]; do
|
|
321
|
+
case "$1" in
|
|
322
|
+
--name) name="$2"; shift 2 ;;
|
|
323
|
+
--content) content="$2"; shift 2 ;;
|
|
324
|
+
--folder) folder="$2"; shift 2 ;;
|
|
325
|
+
*) shift ;;
|
|
326
|
+
esac
|
|
327
|
+
done
|
|
328
|
+
|
|
329
|
+
if [[ -z "$name" ]]; then
|
|
330
|
+
echo "ERROR: --name is required" >&2
|
|
331
|
+
exit 1
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
local metadata
|
|
335
|
+
if [[ -n "$folder" ]]; then
|
|
336
|
+
metadata=$(jq -n --arg name "$name" --arg folder "$folder" \
|
|
337
|
+
'{name: $name, mimeType: "application/vnd.google-apps.document", parents: [$folder]}')
|
|
338
|
+
else
|
|
339
|
+
metadata=$(jq -n --arg name "$name" \
|
|
340
|
+
'{name: $name, mimeType: "application/vnd.google-apps.document"}')
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
local result
|
|
344
|
+
result=$(api_post "${API_BASE}/files?fields=id,name,mimeType,webViewLink" "$metadata")
|
|
345
|
+
echo "$result" | jq .
|
|
346
|
+
|
|
347
|
+
# If content provided, update the document
|
|
348
|
+
if [[ -n "$content" ]]; then
|
|
349
|
+
local file_id
|
|
350
|
+
file_id=$(echo "$result" | jq -r '.id')
|
|
351
|
+
# For Google Docs, we need to use the Docs API to insert content
|
|
352
|
+
# Simpler approach: create as plain text then convert
|
|
353
|
+
echo "Note: Document created. Content insertion for Google Docs requires the Docs API." >&2
|
|
354
|
+
echo "Consider uploading a .txt file with --mime application/vnd.google-apps.document instead." >&2
|
|
355
|
+
fi
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
cmd_move() {
|
|
359
|
+
local file_id="$1"; shift
|
|
360
|
+
local to_folder=""
|
|
361
|
+
while [[ $# -gt 0 ]]; do
|
|
362
|
+
case "$1" in
|
|
363
|
+
--to) to_folder="$2"; shift 2 ;;
|
|
364
|
+
*) shift ;;
|
|
365
|
+
esac
|
|
366
|
+
done
|
|
367
|
+
|
|
368
|
+
if [[ -z "$to_folder" ]]; then
|
|
369
|
+
echo "ERROR: --to folder ID is required" >&2
|
|
370
|
+
exit 1
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
# Get current parents
|
|
374
|
+
local info
|
|
375
|
+
info=$(api_get "${API_BASE}/files/${file_id}?fields=parents")
|
|
376
|
+
local current_parents
|
|
377
|
+
current_parents=$(echo "$info" | jq -r '.parents[]? // empty' | paste -sd, -)
|
|
378
|
+
|
|
379
|
+
local token
|
|
380
|
+
token=$(get_access_token)
|
|
381
|
+
curl -s -X PATCH \
|
|
382
|
+
-H "Authorization: Bearer $token" \
|
|
383
|
+
-H "Content-Type: application/json" \
|
|
384
|
+
"${API_BASE}/files/${file_id}?addParents=${to_folder}&removeParents=${current_parents}&fields=id,name,parents" | jq .
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
cmd_rename() {
|
|
388
|
+
local file_id="$1"; shift
|
|
389
|
+
local new_name=""
|
|
390
|
+
while [[ $# -gt 0 ]]; do
|
|
391
|
+
case "$1" in
|
|
392
|
+
--name) new_name="$2"; shift 2 ;;
|
|
393
|
+
*) shift ;;
|
|
394
|
+
esac
|
|
395
|
+
done
|
|
396
|
+
|
|
397
|
+
if [[ -z "$new_name" ]]; then
|
|
398
|
+
echo "ERROR: --name is required" >&2
|
|
399
|
+
exit 1
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
api_patch "${API_BASE}/files/${file_id}?fields=id,name" \
|
|
403
|
+
"$(jq -n --arg name "$new_name" '{name: $name}')" | jq .
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
cmd_share() {
|
|
407
|
+
local file_id="$1"; shift
|
|
408
|
+
local email="" role="reader" anyone=false
|
|
409
|
+
while [[ $# -gt 0 ]]; do
|
|
410
|
+
case "$1" in
|
|
411
|
+
--email) email="$2"; shift 2 ;;
|
|
412
|
+
--role) role="$2"; shift 2 ;;
|
|
413
|
+
--anyone) anyone=true; shift ;;
|
|
414
|
+
*) shift ;;
|
|
415
|
+
esac
|
|
416
|
+
done
|
|
417
|
+
|
|
418
|
+
local data
|
|
419
|
+
if [[ "$anyone" == "true" ]]; then
|
|
420
|
+
data=$(jq -n --arg role "$role" '{type: "anyone", role: $role}')
|
|
421
|
+
elif [[ -n "$email" ]]; then
|
|
422
|
+
data=$(jq -n --arg email "$email" --arg role "$role" '{type: "user", emailAddress: $email, role: $role}')
|
|
423
|
+
else
|
|
424
|
+
echo "ERROR: --email or --anyone is required" >&2
|
|
425
|
+
exit 1
|
|
426
|
+
fi
|
|
427
|
+
|
|
428
|
+
api_post "${API_BASE}/files/${file_id}/permissions" "$data" | jq .
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
cmd_trash() {
|
|
432
|
+
local file_id="$1"
|
|
433
|
+
api_patch "${API_BASE}/files/${file_id}" '{"trashed": true}' | jq .
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
# --- Dispatch ---
|
|
437
|
+
COMMAND="${1:-}"
|
|
438
|
+
shift || true
|
|
439
|
+
|
|
440
|
+
case "$COMMAND" in
|
|
441
|
+
list) cmd_list "$@" ;;
|
|
442
|
+
search) cmd_search "$@" ;;
|
|
443
|
+
info) cmd_info "$@" ;;
|
|
444
|
+
read) cmd_read "$@" ;;
|
|
445
|
+
download) cmd_download "$@" ;;
|
|
446
|
+
upload) cmd_upload "$@" ;;
|
|
447
|
+
create-doc) cmd_create_doc "$@" ;;
|
|
448
|
+
move) cmd_move "$@" ;;
|
|
449
|
+
rename) cmd_rename "$@" ;;
|
|
450
|
+
share) cmd_share "$@" ;;
|
|
451
|
+
trash) cmd_trash "$@" ;;
|
|
452
|
+
*)
|
|
453
|
+
echo "Usage: drive.sh <command> [options]" >&2
|
|
454
|
+
echo "" >&2
|
|
455
|
+
echo "Commands: list, search, info, read, download, upload," >&2
|
|
456
|
+
echo " create-doc, move, rename, share, trash" >&2
|
|
457
|
+
exit 1
|
|
458
|
+
;;
|
|
459
|
+
esac
|