@ecmaos/coreutils 0.4.0 → 0.4.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/commands/awk.d.ts +4 -0
- package/dist/commands/awk.d.ts.map +1 -0
- package/dist/commands/awk.js +324 -0
- package/dist/commands/awk.js.map +1 -0
- package/dist/commands/chgrp.d.ts +4 -0
- package/dist/commands/chgrp.d.ts.map +1 -0
- package/dist/commands/chgrp.js +187 -0
- package/dist/commands/chgrp.js.map +1 -0
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +139 -2
- package/dist/commands/chmod.js.map +1 -1
- package/dist/commands/chown.d.ts +4 -0
- package/dist/commands/chown.d.ts.map +1 -0
- package/dist/commands/chown.js +257 -0
- package/dist/commands/chown.js.map +1 -0
- package/dist/commands/cksum.d.ts +4 -0
- package/dist/commands/cksum.d.ts.map +1 -0
- package/dist/commands/cksum.js +124 -0
- package/dist/commands/cksum.js.map +1 -0
- package/dist/commands/cmp.d.ts +4 -0
- package/dist/commands/cmp.d.ts.map +1 -0
- package/dist/commands/cmp.js +120 -0
- package/dist/commands/cmp.js.map +1 -0
- package/dist/commands/column.d.ts +4 -0
- package/dist/commands/column.d.ts.map +1 -0
- package/dist/commands/column.js +274 -0
- package/dist/commands/column.js.map +1 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +81 -4
- package/dist/commands/cp.js.map +1 -1
- package/dist/commands/curl.d.ts +4 -0
- package/dist/commands/curl.d.ts.map +1 -0
- package/dist/commands/curl.js +238 -0
- package/dist/commands/curl.js.map +1 -0
- package/dist/commands/du.d.ts +4 -0
- package/dist/commands/du.d.ts.map +1 -0
- package/dist/commands/du.js +168 -0
- package/dist/commands/du.js.map +1 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +125 -2
- package/dist/commands/echo.js.map +1 -1
- package/dist/commands/expand.d.ts +4 -0
- package/dist/commands/expand.d.ts.map +1 -0
- package/dist/commands/expand.js +197 -0
- package/dist/commands/expand.js.map +1 -0
- package/dist/commands/factor.d.ts +4 -0
- package/dist/commands/factor.d.ts.map +1 -0
- package/dist/commands/factor.js +141 -0
- package/dist/commands/factor.js.map +1 -0
- package/dist/commands/fmt.d.ts +4 -0
- package/dist/commands/fmt.d.ts.map +1 -0
- package/dist/commands/fmt.js +278 -0
- package/dist/commands/fmt.js.map +1 -0
- package/dist/commands/fold.d.ts +4 -0
- package/dist/commands/fold.d.ts.map +1 -0
- package/dist/commands/fold.js +253 -0
- package/dist/commands/fold.js.map +1 -0
- package/dist/commands/groups.d.ts +4 -0
- package/dist/commands/groups.d.ts.map +1 -0
- package/dist/commands/groups.js +61 -0
- package/dist/commands/groups.js.map +1 -0
- package/dist/commands/hostname.d.ts +4 -0
- package/dist/commands/hostname.d.ts.map +1 -0
- package/dist/commands/hostname.js +80 -0
- package/dist/commands/hostname.js.map +1 -0
- package/dist/commands/mount.d.ts.map +1 -1
- package/dist/commands/mount.js +192 -97
- package/dist/commands/mount.js.map +1 -1
- package/dist/commands/od.d.ts +4 -0
- package/dist/commands/od.d.ts.map +1 -0
- package/dist/commands/od.js +342 -0
- package/dist/commands/od.js.map +1 -0
- package/dist/commands/pr.d.ts +4 -0
- package/dist/commands/pr.d.ts.map +1 -0
- package/dist/commands/pr.js +298 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/printf.d.ts +4 -0
- package/dist/commands/printf.d.ts.map +1 -0
- package/dist/commands/printf.js +271 -0
- package/dist/commands/printf.js.map +1 -0
- package/dist/commands/readlink.d.ts +4 -0
- package/dist/commands/readlink.d.ts.map +1 -0
- package/dist/commands/readlink.js +104 -0
- package/dist/commands/readlink.js.map +1 -0
- package/dist/commands/realpath.d.ts +4 -0
- package/dist/commands/realpath.d.ts.map +1 -0
- package/dist/commands/realpath.js +111 -0
- package/dist/commands/realpath.js.map +1 -0
- package/dist/commands/rev.d.ts +4 -0
- package/dist/commands/rev.d.ts.map +1 -0
- package/dist/commands/rev.js +134 -0
- package/dist/commands/rev.js.map +1 -0
- package/dist/commands/shuf.d.ts +4 -0
- package/dist/commands/shuf.d.ts.map +1 -0
- package/dist/commands/shuf.js +221 -0
- package/dist/commands/shuf.js.map +1 -0
- package/dist/commands/sleep.d.ts +4 -0
- package/dist/commands/sleep.d.ts.map +1 -0
- package/dist/commands/sleep.js +102 -0
- package/dist/commands/sleep.js.map +1 -0
- package/dist/commands/strings.d.ts +4 -0
- package/dist/commands/strings.d.ts.map +1 -0
- package/dist/commands/strings.js +170 -0
- package/dist/commands/strings.js.map +1 -0
- package/dist/commands/tac.d.ts +4 -0
- package/dist/commands/tac.d.ts.map +1 -0
- package/dist/commands/tac.js +130 -0
- package/dist/commands/tac.js.map +1 -0
- package/dist/commands/time.d.ts +4 -0
- package/dist/commands/time.d.ts.map +1 -0
- package/dist/commands/time.js +126 -0
- package/dist/commands/time.js.map +1 -0
- package/dist/commands/umount.d.ts.map +1 -1
- package/dist/commands/umount.js +2 -3
- package/dist/commands/umount.js.map +1 -1
- package/dist/commands/uname.d.ts +4 -0
- package/dist/commands/uname.d.ts.map +1 -0
- package/dist/commands/uname.js +149 -0
- package/dist/commands/uname.js.map +1 -0
- package/dist/commands/unexpand.d.ts +4 -0
- package/dist/commands/unexpand.d.ts.map +1 -0
- package/dist/commands/unexpand.js +286 -0
- package/dist/commands/unexpand.js.map +1 -0
- package/dist/commands/uptime.d.ts +4 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +62 -0
- package/dist/commands/uptime.js.map +1 -0
- package/dist/commands/yes.d.ts +4 -0
- package/dist/commands/yes.d.ts.map +1 -0
- package/dist/commands/yes.js +58 -0
- package/dist/commands/yes.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/commands/awk.ts +340 -0
- package/src/commands/chmod.ts +141 -2
- package/src/commands/chown.ts +321 -0
- package/src/commands/cksum.ts +133 -0
- package/src/commands/cmp.ts +126 -0
- package/src/commands/column.ts +273 -0
- package/src/commands/cp.ts +93 -4
- package/src/commands/curl.ts +231 -0
- package/src/commands/echo.ts +122 -2
- package/src/commands/expand.ts +207 -0
- package/src/commands/factor.ts +151 -0
- package/src/commands/fmt.ts +293 -0
- package/src/commands/fold.ts +257 -0
- package/src/commands/groups.ts +72 -0
- package/src/commands/hostname.ts +81 -0
- package/src/commands/mount.ts +208 -99
- package/src/commands/od.ts +327 -0
- package/src/commands/pr.ts +291 -0
- package/src/commands/printf.ts +271 -0
- package/src/commands/readlink.ts +102 -0
- package/src/commands/realpath.ts +126 -0
- package/src/commands/rev.ts +143 -0
- package/src/commands/shuf.ts +218 -0
- package/src/commands/sleep.ts +109 -0
- package/src/commands/strings.ts +176 -0
- package/src/commands/tac.ts +138 -0
- package/src/commands/time.ts +144 -0
- package/src/commands/umount.ts +2 -3
- package/src/commands/uname.ts +130 -0
- package/src/commands/unexpand.ts +305 -0
- package/src/commands/uptime.ts +73 -0
- package/src/index.ts +73 -0
- package/tsconfig.json +4 -0
package/src/commands/mount.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
// TODO: Dropbox and S3 are WIP
|
|
2
|
+
|
|
1
3
|
import path from 'path'
|
|
2
4
|
import chalk from 'chalk'
|
|
3
|
-
import { Fetch, InMemory,
|
|
5
|
+
import { Fetch, InMemory, resolveMountConfig, SingleBuffer } from '@zenfs/core'
|
|
4
6
|
import { IndexedDB, WebStorage, WebAccess, /* XML */ } from '@zenfs/dom'
|
|
5
7
|
import { Iso, Zip } from '@zenfs/archives'
|
|
6
8
|
import { Dropbox, /* S3Bucket, */ GoogleDrive } from '@zenfs/cloud'
|
|
@@ -77,7 +79,7 @@ function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
|
77
79
|
Mount a filesystem.
|
|
78
80
|
|
|
79
81
|
Options:
|
|
80
|
-
-t, --type TYPE filesystem type (fetch, indexeddb, webstorage, webaccess, memory, singlebuffer, zip, iso,
|
|
82
|
+
-t, --type TYPE filesystem type (fetch, indexeddb, webstorage, webaccess, memory, singlebuffer, zip, iso, googledrive)
|
|
81
83
|
-o, --options OPTS mount options (comma-separated key=value pairs)
|
|
82
84
|
-a, --all mount all filesystems listed in /etc/fstab
|
|
83
85
|
-l, --list list all mounted filesystems
|
|
@@ -92,20 +94,19 @@ Filesystem types:
|
|
|
92
94
|
singlebuffer mount a filesystem backed by a single buffer
|
|
93
95
|
zip mount a readonly filesystem from a zip archive (requires SOURCE file or URL)
|
|
94
96
|
iso mount a readonly filesystem from an ISO image (requires SOURCE file or URL)
|
|
95
|
-
dropbox mount a Dropbox filesystem (requires client configuration via -o client)
|
|
96
97
|
googledrive mount a Google Drive filesystem (requires apiKey via -o apiKey, optionally clientId for OAuth)
|
|
97
98
|
|
|
98
99
|
Mount options:
|
|
99
100
|
baseUrl=URL base URL for fetch operations (fetch type)
|
|
100
101
|
size=BYTES buffer size in bytes for singlebuffer type (default: 1048576)
|
|
101
102
|
storage=TYPE storage type for webstorage (localStorage or sessionStorage, default: localStorage)
|
|
102
|
-
client=JSON client configuration as JSON string (dropbox type)
|
|
103
103
|
apiKey=KEY Google API key (googledrive type, required)
|
|
104
104
|
clientId=ID Google OAuth client ID (googledrive type, optional)
|
|
105
105
|
scope=SCOPE OAuth scope (googledrive type, default: https://www.googleapis.com/auth/drive)
|
|
106
106
|
cacheTTL=SECONDS cache TTL in seconds for cloud backends (optional)
|
|
107
107
|
|
|
108
108
|
Examples:
|
|
109
|
+
mount -l list all mounted filesystems
|
|
109
110
|
mount -t memory /mnt/tmp mount memory filesystem at /mnt/tmp
|
|
110
111
|
mount -t indexeddb mydb /mnt/db mount IndexedDB store 'mydb' at /mnt/db
|
|
111
112
|
mount -t webstorage /mnt/storage mount WebStorage filesystem using localStorage
|
|
@@ -119,10 +120,8 @@ Examples:
|
|
|
119
120
|
mount -t zip /tmp/archive.zip /mnt/zip
|
|
120
121
|
mount -t iso https://example.com/image.iso /mnt/iso
|
|
121
122
|
mount -t iso /tmp/image.iso /mnt/iso
|
|
122
|
-
mount -t
|
|
123
|
-
mount -t googledrive /mnt/gdrive -o
|
|
124
|
-
mount -t googledrive /mnt/gdrive -o apiKey=YOUR_API_KEY,clientId=YOUR_CLIENT_ID
|
|
125
|
-
mount -l list all mounted filesystems`
|
|
123
|
+
mount -t googledrive /mnt/gdrive -o apiKey=YOUR_API_KEY # readonly/public
|
|
124
|
+
mount -t googledrive /mnt/gdrive -o clientId=YOUR_CLIENT_ID # rw/private`
|
|
126
125
|
writelnStderr(process, terminal, usage)
|
|
127
126
|
}
|
|
128
127
|
|
|
@@ -175,7 +174,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
if (listMode || (argv.length === 0 && !allMode)) {
|
|
178
|
-
const mountList = Array.from(mounts.entries())
|
|
177
|
+
const mountList = Array.from(kernel.filesystem.mounts.entries())
|
|
179
178
|
|
|
180
179
|
if (mountList.length === 0) {
|
|
181
180
|
await writelnStdout(process, terminal, 'No filesystems mounted.')
|
|
@@ -191,13 +190,12 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
191
190
|
|
|
192
191
|
return {
|
|
193
192
|
target: chalk.blue(target),
|
|
194
|
-
type: chalk.gray(backendName.toLowerCase()),
|
|
195
193
|
name: chalk.gray(name)
|
|
196
194
|
}
|
|
197
195
|
})
|
|
198
196
|
|
|
199
197
|
for (const row of mountRows) {
|
|
200
|
-
await writelnStdout(process, terminal, `${row.target.padEnd(30)} ${row.
|
|
198
|
+
await writelnStdout(process, terminal, `${row.target.padEnd(30)} ${row.name}`)
|
|
201
199
|
}
|
|
202
200
|
|
|
203
201
|
return 0
|
|
@@ -982,21 +980,61 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
982
980
|
return 1
|
|
983
981
|
}
|
|
984
982
|
|
|
985
|
-
if (!mountOptions.apiKey) {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
983
|
+
// if (!mountOptions.apiKey) {
|
|
984
|
+
// await writelnStderr(process, terminal, chalk.red('mount: googledrive filesystem requires apiKey option'))
|
|
985
|
+
// await writelnStderr(process, terminal, 'Usage: mount -t googledrive TARGET -o apiKey=YOUR_API_KEY')
|
|
986
|
+
// return 1
|
|
987
|
+
// }
|
|
990
988
|
|
|
991
989
|
const win = window as unknown as {
|
|
992
990
|
gapi?: {
|
|
993
991
|
load?: (module: string, callback: () => void) => void
|
|
994
992
|
client?: {
|
|
995
|
-
init?: (config: { apiKey: string;
|
|
993
|
+
init?: (config: { apiKey: string; discoveryDocs?: string[] }) => Promise<void>
|
|
996
994
|
request?: (config: { path: string }) => Promise<unknown>
|
|
995
|
+
setToken?: (token: { access_token: string }) => void
|
|
997
996
|
drive?: unknown
|
|
998
997
|
}
|
|
999
998
|
}
|
|
999
|
+
google?: {
|
|
1000
|
+
accounts?: {
|
|
1001
|
+
id?: {
|
|
1002
|
+
initialize: (config: { client_id: string; callback: (response: { credential: string }) => void; scope?: string }) => void
|
|
1003
|
+
prompt: (callback?: (notification: { isNotDisplayed: boolean; isSkippedMoment: boolean; isDismissedMoment: boolean }) => void) => void
|
|
1004
|
+
renderButton: (element: HTMLElement, config: { theme?: string; size?: string; text?: string; width?: number; locale?: string }) => void
|
|
1005
|
+
}
|
|
1006
|
+
oauth2?: {
|
|
1007
|
+
initTokenClient: (config: { client_id: string; scope: string; callback: (response: { access_token: string }) => void }) => { requestAccessToken: () => void }
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Load Google Identity Services library if not already loaded
|
|
1014
|
+
if (!win.google?.accounts) {
|
|
1015
|
+
await writelnStdout(process, terminal, chalk.gray('Loading Google Identity Services library...'))
|
|
1016
|
+
|
|
1017
|
+
await new Promise<void>((resolve, reject) => {
|
|
1018
|
+
const script = document.createElement('script')
|
|
1019
|
+
script.src = 'https://accounts.google.com/gsi/client'
|
|
1020
|
+
script.async = true
|
|
1021
|
+
script.defer = true
|
|
1022
|
+
script.onload = () => resolve()
|
|
1023
|
+
script.onerror = () => reject(new Error('Failed to load Google Identity Services script'))
|
|
1024
|
+
document.head.appendChild(script)
|
|
1025
|
+
})
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Wait for google.accounts to be available
|
|
1029
|
+
let attempts = 0
|
|
1030
|
+
while (!win.google?.accounts && attempts < 50) {
|
|
1031
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
1032
|
+
attempts++
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (!win.google?.accounts) {
|
|
1036
|
+
await writelnStderr(process, terminal, chalk.red('mount: Failed to load Google Identity Services library'))
|
|
1037
|
+
return 1
|
|
1000
1038
|
}
|
|
1001
1039
|
|
|
1002
1040
|
// Load Google API script if not already loaded
|
|
@@ -1013,7 +1051,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1013
1051
|
}
|
|
1014
1052
|
|
|
1015
1053
|
// Wait for gapi to be available
|
|
1016
|
-
|
|
1054
|
+
attempts = 0
|
|
1017
1055
|
while (!win.gapi && attempts < 50) {
|
|
1018
1056
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
1019
1057
|
attempts++
|
|
@@ -1024,27 +1062,17 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1024
1062
|
return 1
|
|
1025
1063
|
}
|
|
1026
1064
|
|
|
1027
|
-
// Initialize gapi.client
|
|
1028
1065
|
if (!win.gapi.client || !win.gapi.client.drive) {
|
|
1029
1066
|
await writelnStdout(process, terminal, chalk.gray('Initializing Google API client...'))
|
|
1030
|
-
|
|
1067
|
+
|
|
1031
1068
|
const initConfig: {
|
|
1032
1069
|
apiKey: string
|
|
1033
|
-
clientId?: string
|
|
1034
1070
|
discoveryDocs?: string[]
|
|
1035
|
-
scope?: string
|
|
1036
1071
|
} = {
|
|
1037
|
-
apiKey: mountOptions.apiKey
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
if (mountOptions.clientId) {
|
|
1041
|
-
initConfig.clientId = mountOptions.clientId
|
|
1072
|
+
apiKey: mountOptions.apiKey!,
|
|
1073
|
+
discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']
|
|
1042
1074
|
}
|
|
1043
1075
|
|
|
1044
|
-
const scope = mountOptions.scope || 'https://www.googleapis.com/auth/drive'
|
|
1045
|
-
initConfig.scope = scope
|
|
1046
|
-
initConfig.discoveryDocs = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']
|
|
1047
|
-
|
|
1048
1076
|
// Load the client module
|
|
1049
1077
|
await new Promise<void>((resolve, reject) => {
|
|
1050
1078
|
if (!win.gapi?.load) {
|
|
@@ -1057,47 +1085,62 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1057
1085
|
return
|
|
1058
1086
|
}
|
|
1059
1087
|
win.gapi.client.init(initConfig)
|
|
1060
|
-
.then(() =>
|
|
1061
|
-
|
|
1088
|
+
.then(() => {
|
|
1089
|
+
setTimeout(() => {
|
|
1090
|
+
if (win.gapi?.client?.drive) {
|
|
1091
|
+
resolve()
|
|
1092
|
+
} else {
|
|
1093
|
+
reject(new Error('API discovery response missing required fields. The Drive API discovery document may have failed to load. This could be due to: network issues, invalid API key, or the Drive API not being enabled in your Google Cloud project.'))
|
|
1094
|
+
}
|
|
1095
|
+
}, 1000)
|
|
1096
|
+
})
|
|
1097
|
+
.catch((error: Error & { error?: string; details?: string }) => {
|
|
1098
|
+
const err = error as Error & { error?: string; details?: string }
|
|
1099
|
+
let errorMessage = 'Unknown error'
|
|
1100
|
+
|
|
1101
|
+
errorMessage = JSON.stringify({
|
|
1102
|
+
error: err.error,
|
|
1103
|
+
details: err.details
|
|
1104
|
+
}, null, 2)
|
|
1105
|
+
|
|
1106
|
+
const lowerError = err.error?.toLowerCase()
|
|
1107
|
+
if (lowerError?.includes('discovery') || lowerError?.includes('API') || lowerError?.includes('required fields')) {
|
|
1108
|
+
reject(new Error(`API discovery failed: ${errorMessage}. This could be due to: network issues, invalid API key, or the Drive API not being enabled in your Google Cloud project.`))
|
|
1109
|
+
} else {
|
|
1110
|
+
reject(new Error(err.error || err.details || 'Unknown error'))
|
|
1111
|
+
}
|
|
1112
|
+
})
|
|
1062
1113
|
})
|
|
1063
1114
|
})
|
|
1064
1115
|
|
|
1065
|
-
//
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
// The Drive API should be available after init, but we'll wait a bit
|
|
1073
|
-
setTimeout(() => {
|
|
1074
|
-
const gapi = win.gapi
|
|
1075
|
-
if (gapi?.client?.drive) {
|
|
1076
|
-
resolve()
|
|
1077
|
-
} else if (gapi?.client?.request) {
|
|
1078
|
-
// Try to trigger Drive API loading by making a simple request
|
|
1079
|
-
gapi.client.request({
|
|
1080
|
-
path: 'https://www.googleapis.com/drive/v3/about?fields=user'
|
|
1081
|
-
}).then(() => {
|
|
1116
|
+
// Verify Drive API is loaded
|
|
1117
|
+
if (!win.gapi?.client?.drive) {
|
|
1118
|
+
// Try one more time with a longer wait
|
|
1119
|
+
await new Promise<void>((resolve, reject) => {
|
|
1120
|
+
let attempts = 0
|
|
1121
|
+
const checkDrive = () => {
|
|
1122
|
+
if (win.gapi?.client?.drive) {
|
|
1082
1123
|
resolve()
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
}
|
|
1090
|
-
})
|
|
1091
|
-
} else {
|
|
1092
|
-
reject(new Error('gapi.client.request is not available'))
|
|
1124
|
+
} else if (attempts < 10) {
|
|
1125
|
+
attempts++
|
|
1126
|
+
setTimeout(checkDrive, 200)
|
|
1127
|
+
} else {
|
|
1128
|
+
reject(new Error('Failed to load Drive API. The discovery document may have failed to load. Check the browser console for network errors (502 Bad Gateway suggests a network issue).'))
|
|
1129
|
+
}
|
|
1093
1130
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1131
|
+
checkDrive()
|
|
1132
|
+
})
|
|
1133
|
+
}
|
|
1096
1134
|
}
|
|
1097
1135
|
|
|
1098
1136
|
if (!win.gapi?.client?.drive) {
|
|
1099
1137
|
await writelnStderr(process, terminal, chalk.red('mount: Google Drive API is not available'))
|
|
1100
|
-
await writelnStderr(process, terminal, '
|
|
1138
|
+
await writelnStderr(process, terminal, chalk.yellow('Troubleshooting steps:'))
|
|
1139
|
+
await writelnStderr(process, terminal, ' 1. Check the browser console for network errors (502 Bad Gateway suggests a network/server issue)')
|
|
1140
|
+
await writelnStderr(process, terminal, ' 2. Verify your API key is valid and has the Drive API enabled')
|
|
1141
|
+
await writelnStderr(process, terminal, ' 3. Ensure the Drive API is enabled in your Google Cloud project')
|
|
1142
|
+
await writelnStderr(process, terminal, ' 4. Check if there are any API key restrictions (HTTP referrers, IP addresses, etc.)')
|
|
1143
|
+
await writelnStderr(process, terminal, ' 5. Try refreshing the page and mounting again')
|
|
1101
1144
|
return 1
|
|
1102
1145
|
}
|
|
1103
1146
|
|
|
@@ -1107,7 +1150,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1107
1150
|
|
|
1108
1151
|
const driveScope = mountOptions.scope || 'https://www.googleapis.com/auth/drive'
|
|
1109
1152
|
|
|
1110
|
-
// Check if user is already
|
|
1153
|
+
// Check if user is already authenticated
|
|
1111
1154
|
try {
|
|
1112
1155
|
const client = win.gapi?.client
|
|
1113
1156
|
if (client?.request) {
|
|
@@ -1116,58 +1159,124 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1116
1159
|
})
|
|
1117
1160
|
}
|
|
1118
1161
|
} catch (error) {
|
|
1119
|
-
// User needs to authenticate
|
|
1120
|
-
await writelnStdout(process, terminal, chalk.
|
|
1162
|
+
// User needs to authenticate using Google Identity Services
|
|
1163
|
+
await writelnStdout(process, terminal, chalk.gray('Authentication required. Please sign in to Google...'))
|
|
1121
1164
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
// Fallback: try to trigger auth flow
|
|
1165
|
+
try {
|
|
1166
|
+
if (!win.google?.accounts?.oauth2) {
|
|
1167
|
+
throw new Error('Google Identity Services OAuth2 is not available')
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1128
1170
|
await new Promise<void>((resolve, reject) => {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
if (auth2.getAuthInstance) {
|
|
1141
|
-
const auth = auth2.getAuthInstance()
|
|
1142
|
-
auth.signIn().then(() => resolve()).catch(reject)
|
|
1143
|
-
} else {
|
|
1144
|
-
resolve()
|
|
1145
|
-
}
|
|
1146
|
-
}).catch(reject)
|
|
1147
|
-
} else {
|
|
1148
|
-
resolve()
|
|
1149
|
-
}
|
|
1171
|
+
const tokenClient = win.google?.accounts?.oauth2?.initTokenClient({
|
|
1172
|
+
client_id: mountOptions.clientId!,
|
|
1173
|
+
scope: driveScope,
|
|
1174
|
+
callback: (response: { access_token: string }) => {
|
|
1175
|
+
if (response.access_token && win.gapi?.client?.setToken) {
|
|
1176
|
+
win.gapi.client.setToken({ access_token: response.access_token })
|
|
1177
|
+
resolve()
|
|
1178
|
+
} else {
|
|
1179
|
+
reject(new Error('Failed to obtain access token'))
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1150
1182
|
})
|
|
1183
|
+
|
|
1184
|
+
tokenClient?.requestAccessToken()
|
|
1151
1185
|
})
|
|
1186
|
+
} catch (authError) {
|
|
1187
|
+
const authErrMsg = authError instanceof Error ? authError.message : String(authError)
|
|
1188
|
+
if (authErrMsg.toLowerCase().includes('popup') || authErrMsg.toLowerCase().includes('blocked')) {
|
|
1189
|
+
throw new Error(`OAuth authentication failed: ${authErrMsg}. Please allow popups for this site.`)
|
|
1190
|
+
} else if (authErrMsg.toLowerCase().includes('origin') || authErrMsg.toLowerCase().includes('authorized')) {
|
|
1191
|
+
throw new Error(`OAuth authentication failed: ${authErrMsg}. Your origin may not be authorized in Google Cloud Console. Add your current origin to the OAuth client's authorized JavaScript origins.`)
|
|
1192
|
+
}
|
|
1193
|
+
throw authError
|
|
1152
1194
|
}
|
|
1153
1195
|
}
|
|
1154
1196
|
}
|
|
1155
1197
|
|
|
1156
1198
|
const drive = win.gapi.client.drive
|
|
1157
|
-
const cacheTTL = mountOptions.cacheTTL ? parseInt(mountOptions.cacheTTL
|
|
1199
|
+
const cacheTTL = mountOptions.cacheTTL ? parseInt(mountOptions.cacheTTL) : undefined
|
|
1158
1200
|
|
|
1159
1201
|
await kernel.filesystem.fsSync.mount(
|
|
1160
1202
|
target,
|
|
1161
1203
|
await resolveMountConfig({
|
|
1162
1204
|
backend: GoogleDrive,
|
|
1163
|
-
drive: drive as never,
|
|
1205
|
+
drive: drive as never,
|
|
1206
|
+
disableAsyncCache: true,
|
|
1164
1207
|
...(cacheTTL && !isNaN(cacheTTL) ? { cacheTTL } : {})
|
|
1165
1208
|
})
|
|
1166
1209
|
)
|
|
1167
1210
|
} catch (error) {
|
|
1168
|
-
|
|
1169
|
-
if (error instanceof Error
|
|
1170
|
-
|
|
1211
|
+
let errorMessage = 'Unknown error'
|
|
1212
|
+
if (error instanceof Error) {
|
|
1213
|
+
errorMessage = error.message || error.toString()
|
|
1214
|
+
} else if (typeof error === 'string') {
|
|
1215
|
+
errorMessage = error
|
|
1216
|
+
} else if (error && typeof error === 'object') {
|
|
1217
|
+
// Try to extract error message from object
|
|
1218
|
+
const err = error as Record<string, unknown>
|
|
1219
|
+
errorMessage =
|
|
1220
|
+
(typeof err.message === 'string' ? err.message : '') ||
|
|
1221
|
+
(typeof err.error === 'string' ? err.error : '') ||
|
|
1222
|
+
(typeof err.details === 'string' ? err.details : '') ||
|
|
1223
|
+
(typeof err.reason === 'string' ? err.reason : '') ||
|
|
1224
|
+
(err.toString && typeof err.toString === 'function' ? err.toString() : '') ||
|
|
1225
|
+
JSON.stringify(error)
|
|
1226
|
+
} else {
|
|
1227
|
+
errorMessage = String(error)
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
await writelnStderr(process, terminal, chalk.red(`mount: failed to mount googledrive filesystem: ${errorMessage}`))
|
|
1231
|
+
|
|
1232
|
+
// Provide specific guidance for common errors
|
|
1233
|
+
const lowerMessage = errorMessage.toLowerCase()
|
|
1234
|
+
if (lowerMessage.includes('popup') || lowerMessage.includes('blocked')) {
|
|
1235
|
+
await writelnStderr(process, terminal, chalk.yellow('\nOAuth popup was blocked. Common causes:'))
|
|
1236
|
+
await writelnStderr(process, terminal, ' • Browser popup blocker is enabled')
|
|
1237
|
+
await writelnStderr(process, terminal, ' • Browser security restrictions')
|
|
1238
|
+
await writelnStderr(process, terminal, chalk.gray('\nTo fix this:'))
|
|
1239
|
+
await writelnStderr(process, terminal, ' 1. Allow popups for this site in your browser settings')
|
|
1240
|
+
await writelnStderr(process, terminal, ' 2. Try the mount command again')
|
|
1241
|
+
} else if (lowerMessage.includes('origin') || lowerMessage.includes('authorized')) {
|
|
1242
|
+
await writelnStderr(process, terminal, chalk.yellow('\nOAuth authentication failed. Common causes:'))
|
|
1243
|
+
await writelnStderr(process, terminal, ' • Your domain/origin is not authorized in Google Cloud Console')
|
|
1244
|
+
await writelnStderr(process, terminal, ' • Invalid or incorrect OAuth client ID')
|
|
1245
|
+
await writelnStderr(process, terminal, chalk.gray('\nTo fix this:'))
|
|
1246
|
+
await writelnStderr(process, terminal, ' 1. Go to Google Cloud Console > APIs & Services > Credentials')
|
|
1247
|
+
await writelnStderr(process, terminal, ' 2. Find your OAuth 2.0 Client ID')
|
|
1248
|
+
await writelnStderr(process, terminal, ' 3. Add your current origin to "Authorized JavaScript origins"')
|
|
1249
|
+
await writelnStderr(process, terminal, ' (e.g., http://localhost:30443 or your domain)')
|
|
1250
|
+
await writelnStderr(process, terminal, ' 4. If you only need read-only access, try mounting without clientId:')
|
|
1251
|
+
await writelnStderr(process, terminal, ' mount -t googledrive /mnt/gdrive -o apiKey=YOUR_API_KEY')
|
|
1252
|
+
} else if (lowerMessage.includes('discovery') || lowerMessage.includes('api discovery') || lowerMessage.includes('required fields')) {
|
|
1253
|
+
await writelnStderr(process, terminal, chalk.yellow('\nThe Drive API discovery document failed to load. Common causes:'))
|
|
1254
|
+
await writelnStderr(process, terminal, ' • Network connectivity issues (check for 502 Bad Gateway in console)')
|
|
1255
|
+
await writelnStderr(process, terminal, ' • Invalid or restricted API key')
|
|
1256
|
+
await writelnStderr(process, terminal, ' • Drive API not enabled in Google Cloud project')
|
|
1257
|
+
await writelnStderr(process, terminal, ' • CORS or browser security restrictions')
|
|
1258
|
+
await writelnStderr(process, terminal, chalk.gray('\nCheck the browser console for detailed network error messages.'))
|
|
1259
|
+
} else if (lowerMessage.includes('network') || lowerMessage.includes('fetch') || lowerMessage.includes('502') || lowerMessage.includes('bad gateway')) {
|
|
1260
|
+
await writelnStderr(process, terminal, chalk.yellow('\nNetwork error detected. This may be a temporary issue with Google\'s servers.'))
|
|
1261
|
+
await writelnStderr(process, terminal, ' • Wait a few moments and try again')
|
|
1262
|
+
await writelnStderr(process, terminal, ' • Check your internet connection')
|
|
1263
|
+
await writelnStderr(process, terminal, ' • Verify the API key is correct')
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Show full error details if it's an object (for debugging)
|
|
1267
|
+
if (error && typeof error === 'object' && !(error instanceof Error)) {
|
|
1268
|
+
try {
|
|
1269
|
+
const errorStr = JSON.stringify(error, null, 2)
|
|
1270
|
+
if (errorStr !== '{}' && errorStr.length < 500) {
|
|
1271
|
+
await writelnStderr(process, terminal, chalk.gray(`\nError details:\n${errorStr}`))
|
|
1272
|
+
}
|
|
1273
|
+
} catch {
|
|
1274
|
+
// Ignore JSON stringify errors
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (error instanceof Error && error.stack && !lowerMessage.includes('discovery') && !lowerMessage.includes('network')) {
|
|
1279
|
+
await writelnStderr(process, terminal, chalk.gray(`\nStack trace:\n${error.stack}`))
|
|
1171
1280
|
}
|
|
1172
1281
|
return 1
|
|
1173
1282
|
}
|
|
@@ -1175,7 +1284,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
1175
1284
|
}
|
|
1176
1285
|
default:
|
|
1177
1286
|
await writelnStderr(process, terminal, chalk.red(`mount: unknown filesystem type '${type}'`))
|
|
1178
|
-
await writelnStderr(process, terminal, 'Supported types: fetch, indexeddb, webstorage, webaccess, memory, singlebuffer, zip, iso, dropbox,
|
|
1287
|
+
await writelnStderr(process, terminal, 'Supported types: fetch, indexeddb, webstorage, webaccess, memory, singlebuffer, zip, iso, dropbox, s3, googledrive')
|
|
1179
1288
|
return 1
|
|
1180
1289
|
}
|
|
1181
1290
|
|