phio 0.0.1 → 0.1.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.
- package/README.md +7 -0
- package/package.json +20 -8
- package/src/commands/DevCommand.ts +94 -0
- package/src/commands/LinkCommand.ts +21 -0
- package/src/commands/ListCommand.ts +21 -0
- package/src/{LoginCommand.ts → commands/LoginCommand.ts} +3 -5
- package/src/{LogsCommand.ts → commands/LogsCommand.ts} +5 -4
- package/src/commands/WhoAmICommand.ts +15 -0
- package/src/global.d.ts +2 -5
- package/src/index.ts +9 -3
- package/src/lib/InstanceFields.ts +6 -0
- package/src/{getClient.ts → lib/getClient.ts} +9 -0
- package/src/DevCommand.ts +0 -67
- /package/src/{Task.ts → lib/Task.ts} +0 -0
- /package/src/{config.ts → lib/config.ts} +0 -0
- /package/src/{constants.ts → lib/constants.ts} +0 -0
- /package/src/{defaultInstanceId.ts → lib/defaultInstanceId.ts} +0 -0
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phio",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "A CLI tool to manage your PocketHost instances",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/
|
|
7
|
+
"url": "https://github.com/pockethost/phio.git"
|
|
8
8
|
},
|
|
9
|
-
"homepage": "https://github.com/
|
|
9
|
+
"homepage": "https://github.com/pockethost/phio",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"cli",
|
|
12
12
|
"pocketbase",
|
|
@@ -15,17 +15,18 @@
|
|
|
15
15
|
"author": "Ben Allfree",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"bugs": {
|
|
18
|
-
"url": "https://github.com/
|
|
18
|
+
"url": "https://github.com/pockethost/phio/issues"
|
|
19
19
|
},
|
|
20
20
|
"module": "src/index.ts",
|
|
21
21
|
"type": "module",
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/bun": "latest",
|
|
24
24
|
"@types/fs-extra": "^11.0.4",
|
|
25
|
-
"@types/node": "^22.5.5"
|
|
25
|
+
"@types/node": "^22.5.5",
|
|
26
|
+
"prettier-plugin-organize-imports": "^4.0.0"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
|
-
"typescript": "^5.
|
|
29
|
+
"typescript": "^5.6.2"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
32
|
"dev": "tsx --watch ./src/index.ts"
|
|
@@ -39,8 +40,9 @@
|
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@inquirer/prompts": "^5.5.0",
|
|
41
42
|
"@s-libs/micro-dash": "^18.0.0",
|
|
42
|
-
"@samkirkland/ftp-deploy": "
|
|
43
|
+
"@samkirkland/ftp-deploy": "github:benallfree/ftp-deploy#glob",
|
|
43
44
|
"@sentool/fetch-event-source": "^0.5.0",
|
|
45
|
+
"bottleneck": "^2.19.5",
|
|
44
46
|
"chokidar": "^4.0.0",
|
|
45
47
|
"commander": "^12.1.0",
|
|
46
48
|
"email-validator": "^2.0.4",
|
|
@@ -48,7 +50,17 @@
|
|
|
48
50
|
"env-var": "^7.5.0",
|
|
49
51
|
"event-stream": "^4.0.1",
|
|
50
52
|
"fs-extra": "^11.2.0",
|
|
53
|
+
"multimatch": "^7.0.0",
|
|
51
54
|
"ora": "^8.1.0",
|
|
52
|
-
"pocketbase": "^0.21.5"
|
|
55
|
+
"pocketbase": "^0.21.5",
|
|
56
|
+
"tsx": "^4.19.1"
|
|
57
|
+
},
|
|
58
|
+
"prettier": {
|
|
59
|
+
"semi": false,
|
|
60
|
+
"singleQuote": true,
|
|
61
|
+
"trailingComma": "es5",
|
|
62
|
+
"plugins": [
|
|
63
|
+
"prettier-plugin-organize-imports"
|
|
64
|
+
]
|
|
53
65
|
}
|
|
54
66
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { debounce } from '@s-libs/micro-dash'
|
|
2
|
+
import { deploy, excludeDefaults } from '@samkirkland/ftp-deploy'
|
|
3
|
+
import Bottleneck from 'bottleneck'
|
|
4
|
+
import { watch } from 'chokidar'
|
|
5
|
+
import { Command } from 'commander'
|
|
6
|
+
import { ensureDirSync } from 'fs-extra'
|
|
7
|
+
import multimatch from 'multimatch'
|
|
8
|
+
import { config } from '../lib/config'
|
|
9
|
+
import { getInstanceBySubdomainCnameOrId } from '../lib/getClient'
|
|
10
|
+
import { defaultInstanceId } from './../lib/defaultInstanceId'
|
|
11
|
+
|
|
12
|
+
async function deployMyCode(
|
|
13
|
+
instanceName: string,
|
|
14
|
+
include: string[],
|
|
15
|
+
exclude: string[]
|
|
16
|
+
) {
|
|
17
|
+
const cachePath = '.cache'
|
|
18
|
+
ensureDirSync(cachePath)
|
|
19
|
+
|
|
20
|
+
console.log('🚚 Deploy started')
|
|
21
|
+
await deploy({
|
|
22
|
+
server: 'ftp.pockethost.io',
|
|
23
|
+
username: `__auth__`,
|
|
24
|
+
password: config(`auth`)!.token,
|
|
25
|
+
'server-dir': `${instanceName}/`,
|
|
26
|
+
include,
|
|
27
|
+
exclude: [...excludeDefaults, ...exclude],
|
|
28
|
+
'state-name': '.cache/.ftp-deploy-sync-state.json',
|
|
29
|
+
'log-level': 'verbose',
|
|
30
|
+
})
|
|
31
|
+
console.log('🚀 Deploy done!')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const DevCommand = () => {
|
|
35
|
+
return new Command('dev')
|
|
36
|
+
.argument(`[instanceId]`, `Instance name`, defaultInstanceId())
|
|
37
|
+
.description(`Watch for local modifications and sync to remote`)
|
|
38
|
+
.option(
|
|
39
|
+
'-i, --include <include...>',
|
|
40
|
+
'Files to include in the sync',
|
|
41
|
+
(val, prev) => [...prev, val],
|
|
42
|
+
['pb_*/**/*', `package.json`, `bun.lockb`]
|
|
43
|
+
)
|
|
44
|
+
.option(
|
|
45
|
+
'-e, --exclude <exclude...>',
|
|
46
|
+
'Files to exclude from the sync',
|
|
47
|
+
(val, prev) => [...prev, val],
|
|
48
|
+
[`pb_data/**/*`]
|
|
49
|
+
)
|
|
50
|
+
.action(async (_instanceId, { include, exclude }) => {
|
|
51
|
+
if (!_instanceId) {
|
|
52
|
+
console.error(
|
|
53
|
+
`No instance name provided and none was found in package.json or pockethost.json. Use 'phio link <instance>'`
|
|
54
|
+
)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
console.log(`Dev mode`)
|
|
58
|
+
console.log({ include, exclude })
|
|
59
|
+
|
|
60
|
+
const instance = await getInstanceBySubdomainCnameOrId(_instanceId)
|
|
61
|
+
|
|
62
|
+
const limiter = new Bottleneck({ maxConcurrent: 1 })
|
|
63
|
+
const upload = debounce(
|
|
64
|
+
() =>
|
|
65
|
+
limiter.schedule(() =>
|
|
66
|
+
deployMyCode(instance.subdomain, include, exclude).catch(
|
|
67
|
+
console.error
|
|
68
|
+
)
|
|
69
|
+
),
|
|
70
|
+
200
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const watcher = watch('.', {
|
|
74
|
+
persistent: true,
|
|
75
|
+
ignored: (file) => {
|
|
76
|
+
if (file === '.') return false
|
|
77
|
+
const isIgnored =
|
|
78
|
+
multimatch([file], include).length === 0 ||
|
|
79
|
+
multimatch([file], exclude).length > 0
|
|
80
|
+
console.log({ file, isIgnored })
|
|
81
|
+
return isIgnored
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
console.log(
|
|
85
|
+
`Watching for changes in ${include.join(', ')} and excluding ${exclude.join(', ')}`
|
|
86
|
+
)
|
|
87
|
+
const handle = (path: string, details: any) => {
|
|
88
|
+
upload()
|
|
89
|
+
// internal
|
|
90
|
+
console.log(`Syncing ${path}`)
|
|
91
|
+
}
|
|
92
|
+
watcher.on('add', handle).on('change', handle).on('unlink', handle)
|
|
93
|
+
})
|
|
94
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { readJSONSync, writeJSONSync } from 'fs-extra/esm'
|
|
3
|
+
import { getInstanceBySubdomainCnameOrId } from './../lib/getClient'
|
|
4
|
+
|
|
5
|
+
export const LinkCommand = () => {
|
|
6
|
+
return new Command(`link`)
|
|
7
|
+
.argument(`<instance>`, `Instance name or ID`)
|
|
8
|
+
.description(`Link a local directory to a remote instance`)
|
|
9
|
+
.action(async (_anyName) => {
|
|
10
|
+
const instance = await getInstanceBySubdomainCnameOrId(_anyName)
|
|
11
|
+
if (!instance) {
|
|
12
|
+
console.error(`Instance not found`)
|
|
13
|
+
process.exit(1)
|
|
14
|
+
}
|
|
15
|
+
console.log(`Instance found: ${instance.subdomain}`)
|
|
16
|
+
const pkg = readJSONSync(`package.json`)
|
|
17
|
+
pkg.pockethost = { instanceId: instance.subdomain }
|
|
18
|
+
console.log(`Writing to package.json`)
|
|
19
|
+
writeJSONSync(`package.json`, pkg, { spaces: 2 })
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { getClient } from './../lib/getClient'
|
|
3
|
+
import { InstanceFields } from './../lib/InstanceFields'
|
|
4
|
+
|
|
5
|
+
export const ListCommand = () => {
|
|
6
|
+
return new Command(`list`)
|
|
7
|
+
.description(`List all the logs`)
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const client = getClient()
|
|
10
|
+
const instances = await client
|
|
11
|
+
.collection(`instances`)
|
|
12
|
+
.getFullList<InstanceFields>()
|
|
13
|
+
instances.forEach((instance) => {
|
|
14
|
+
console.log(
|
|
15
|
+
`- ${instance.subdomain} (${instance.id}) ${
|
|
16
|
+
instance.cname ? `(${instance.cname})` : ''
|
|
17
|
+
} (${instance.status.toUpperCase()})`
|
|
18
|
+
)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { input, password } from '@inquirer/prompts'
|
|
2
2
|
import { Command } from 'commander'
|
|
3
3
|
import * as EmailValidator from 'email-validator'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { getClient } from './getClient'
|
|
4
|
+
import { config } from '../lib/config'
|
|
5
|
+
import { getClient } from './../lib/getClient'
|
|
6
|
+
import { runTasks } from './../lib/Task'
|
|
8
7
|
|
|
9
8
|
export const LoginCommand = () =>
|
|
10
9
|
new Command('login')
|
|
@@ -38,7 +37,6 @@ export const LoginCommand = () =>
|
|
|
38
37
|
const res = await client
|
|
39
38
|
.collection('users')
|
|
40
39
|
.authWithPassword(email, pw)
|
|
41
|
-
console.log({ res })
|
|
42
40
|
},
|
|
43
41
|
},
|
|
44
42
|
])
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
2
|
-
import { config } from './config'
|
|
3
|
-
//@ts-ignore
|
|
4
1
|
import { fetchEventSource } from '@sentool/fetch-event-source'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { config } from '../lib/config'
|
|
4
|
+
import { defaultInstanceId } from '../lib/defaultInstanceId'
|
|
5
5
|
|
|
6
6
|
export enum StreamNames {
|
|
7
7
|
StdOut = 'stdout',
|
|
@@ -45,6 +45,7 @@ const watchInstanceLog = (
|
|
|
45
45
|
},
|
|
46
46
|
onerror: (e: Error) => {
|
|
47
47
|
console.error(`got an error`, e)
|
|
48
|
+
setTimeout(continuallyFetchFromEventSource, 100)
|
|
48
49
|
},
|
|
49
50
|
onclose: () => {
|
|
50
51
|
console.log(`closed`)
|
|
@@ -63,7 +64,7 @@ const watchInstanceLog = (
|
|
|
63
64
|
export const LogsCommand = () => {
|
|
64
65
|
return new Command('logs')
|
|
65
66
|
.description(`Tail instance logs`)
|
|
66
|
-
.argument('
|
|
67
|
+
.argument('[instance]', 'Instance ID', defaultInstanceId())
|
|
67
68
|
.action((instance) => {
|
|
68
69
|
watchInstanceLog(instance, (log) => {
|
|
69
70
|
const { time, message, stream } = log
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { config } from '../lib/config'
|
|
3
|
+
|
|
4
|
+
export const WhoAmICommand = () => {
|
|
5
|
+
return new Command(`whoami`)
|
|
6
|
+
.description(`Show the current user`)
|
|
7
|
+
.action(() => {
|
|
8
|
+
const email = config(`email`)
|
|
9
|
+
if (email) {
|
|
10
|
+
console.log(`Current user is: ${email}`)
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
console.log(`No user is currently logged in`)
|
|
14
|
+
})
|
|
15
|
+
}
|
package/src/global.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
declare module '
|
|
2
|
-
function
|
|
3
|
-
function writeJSONSync(path: string, data: any): void
|
|
4
|
-
function ensureDirSync(path: string): void
|
|
5
|
-
export { readJSONSync, writeJSONSync, ensureDirSync }
|
|
1
|
+
declare module '@sentool/fetch-event-source' {
|
|
2
|
+
export function fetchEventSource(url: string, options: any): void
|
|
6
3
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
2
|
import { program } from 'commander'
|
|
3
|
-
import { LoginCommand } from './LoginCommand'
|
|
4
3
|
import { version } from '../package.json'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { DevCommand } from './commands/DevCommand'
|
|
5
|
+
import { LinkCommand } from './commands/LinkCommand'
|
|
6
|
+
import { ListCommand } from './commands/ListCommand'
|
|
7
|
+
import { LoginCommand } from './commands/LoginCommand'
|
|
8
|
+
import { LogsCommand } from './commands/LogsCommand'
|
|
9
|
+
import { WhoAmICommand } from './commands/WhoAmICommand'
|
|
7
10
|
|
|
8
11
|
program
|
|
9
12
|
.name(`PocketHost CLI`)
|
|
@@ -12,5 +15,8 @@ program
|
|
|
12
15
|
.addCommand(LoginCommand())
|
|
13
16
|
.addCommand(LogsCommand())
|
|
14
17
|
.addCommand(DevCommand())
|
|
18
|
+
.addCommand(WhoAmICommand())
|
|
19
|
+
.addCommand(ListCommand())
|
|
20
|
+
.addCommand(LinkCommand())
|
|
15
21
|
|
|
16
22
|
program.parseAsync(process.argv).catch(console.error)
|
|
@@ -15,3 +15,12 @@ export const getClient = () => {
|
|
|
15
15
|
}
|
|
16
16
|
return client
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export const getInstanceBySubdomainCnameOrId = async (search: string) => {
|
|
20
|
+
const client = getClient()
|
|
21
|
+
return await client
|
|
22
|
+
.collection(`instances`)
|
|
23
|
+
.getFirstListItem(
|
|
24
|
+
`id='${search}' || subdomain='${search}' || cname='${search}'`
|
|
25
|
+
)
|
|
26
|
+
}
|
package/src/DevCommand.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
2
|
-
import { config } from './config'
|
|
3
|
-
import { deploy, excludeDefaults } from '@samkirkland/ftp-deploy'
|
|
4
|
-
import { getClient } from './getClient'
|
|
5
|
-
import { ensureDirSync } from 'fs-extra'
|
|
6
|
-
import { defaultInstanceId } from './defaultInstanceId'
|
|
7
|
-
import { watch } from 'chokidar'
|
|
8
|
-
import { file } from 'bun'
|
|
9
|
-
import { basename, dirname } from 'path'
|
|
10
|
-
import { debounce } from '@s-libs/micro-dash'
|
|
11
|
-
|
|
12
|
-
async function deployMyCode(instanceName: string) {
|
|
13
|
-
const cachePath = '.cache'
|
|
14
|
-
ensureDirSync(cachePath)
|
|
15
|
-
|
|
16
|
-
console.log('🚚 Deploy started')
|
|
17
|
-
await deploy({
|
|
18
|
-
server: 'ftp.pockethost.io',
|
|
19
|
-
username: `__auth__`,
|
|
20
|
-
password: config(`auth`)!.token,
|
|
21
|
-
'server-dir': `${instanceName}/`,
|
|
22
|
-
exclude: ['*', '!pb_*/**/*'],
|
|
23
|
-
'state-name': '.cache/.ftp-deploy-sync-state.json',
|
|
24
|
-
'log-level': 'verbose',
|
|
25
|
-
})
|
|
26
|
-
console.log('🚀 Deploy done!')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const DevCommand = () => {
|
|
30
|
-
return new Command('dev')
|
|
31
|
-
.argument(`[instanceId]`, `Instance name`, defaultInstanceId())
|
|
32
|
-
.description(`Watch for local modifications and sync to remote`)
|
|
33
|
-
.action(async (_instanceId) => {
|
|
34
|
-
if (!_instanceId) {
|
|
35
|
-
console.error(
|
|
36
|
-
'No instance name provided and none was found in package.json or pockethost.json'
|
|
37
|
-
)
|
|
38
|
-
process.exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const client = getClient()
|
|
42
|
-
|
|
43
|
-
const instance = await client
|
|
44
|
-
.collection(`instances`)
|
|
45
|
-
.getFirstListItem(`id='${_instanceId}' || subdomain='${_instanceId}'`)
|
|
46
|
-
|
|
47
|
-
const upload = debounce(() => {
|
|
48
|
-
deployMyCode(instance.subdomain).catch(console.error)
|
|
49
|
-
}, 200)
|
|
50
|
-
|
|
51
|
-
const watcher = watch(['.'], {
|
|
52
|
-
persistent: true,
|
|
53
|
-
ignored: (file) => {
|
|
54
|
-
const isIgnored = file !== '.' && !file.startsWith('pb_')
|
|
55
|
-
// console.log({ file, isIgnored })
|
|
56
|
-
return isIgnored
|
|
57
|
-
},
|
|
58
|
-
})
|
|
59
|
-
console.log(`Watching for changes in pb_*/**/*`)
|
|
60
|
-
const handle = (path: string, details: any) => {
|
|
61
|
-
upload()
|
|
62
|
-
// internal
|
|
63
|
-
console.log({ path, details })
|
|
64
|
-
}
|
|
65
|
-
watcher.on('add', handle).on('change', handle).on('unlink', handle)
|
|
66
|
-
})
|
|
67
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|