phio 0.1.2 → 0.2.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 +2 -2
- package/package.json +4 -2
- package/src/cli.ts +24 -0
- package/src/commands/DeployCommand.ts +26 -0
- package/src/commands/DevCommand.ts +83 -63
- package/src/commands/LinkCommand.ts +50 -12
- package/src/commands/LoginCommand.ts +47 -45
- package/src/commands/LogsCommand.ts +2 -2
- package/src/index.ts +9 -20
- package/src/lib/config.ts +1 -0
- package/src/lib/defaultInstanceId.ts +1 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A CLI tool to manage your PocketHost instances",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
"bugs": {
|
|
18
18
|
"url": "https://github.com/pockethost/phio/issues"
|
|
19
19
|
},
|
|
20
|
+
"main": "src/index.ts",
|
|
20
21
|
"module": "src/index.ts",
|
|
21
22
|
"type": "module",
|
|
23
|
+
"types": "src/index.ts",
|
|
22
24
|
"devDependencies": {
|
|
23
25
|
"@types/bun": "latest",
|
|
24
26
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
"dev": "tsx --watch ./src/index.ts"
|
|
33
35
|
},
|
|
34
36
|
"bin": {
|
|
35
|
-
"phio": "src/
|
|
37
|
+
"phio": "src/cli.ts"
|
|
36
38
|
},
|
|
37
39
|
"files": [
|
|
38
40
|
"src"
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { program } from 'commander'
|
|
3
|
+
import { version } from '../package.json'
|
|
4
|
+
import { DeployCommand } from './commands/DeployCommand'
|
|
5
|
+
import { DevCommand } from './commands/DevCommand'
|
|
6
|
+
import { LinkCommand } from './commands/LinkCommand'
|
|
7
|
+
import { ListCommand } from './commands/ListCommand'
|
|
8
|
+
import { LoginCommand } from './commands/LoginCommand'
|
|
9
|
+
import { LogsCommand } from './commands/LogsCommand'
|
|
10
|
+
import { WhoAmICommand } from './commands/WhoAmICommand'
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name(`PocketHost CLI`)
|
|
14
|
+
.version(version)
|
|
15
|
+
.description(`CLI access to phio`)
|
|
16
|
+
.addCommand(LoginCommand())
|
|
17
|
+
.addCommand(LogsCommand())
|
|
18
|
+
.addCommand(DevCommand())
|
|
19
|
+
.addCommand(WhoAmICommand())
|
|
20
|
+
.addCommand(ListCommand())
|
|
21
|
+
.addCommand(LinkCommand())
|
|
22
|
+
.addCommand(DeployCommand())
|
|
23
|
+
|
|
24
|
+
program.parseAsync(process.argv).catch(console.error)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { savedInstanceId } from '../lib/defaultInstanceId'
|
|
3
|
+
import { DEFAULT_EXCLUDES, DEFAULT_INCLUDES, deployMyCode } from './DevCommand'
|
|
4
|
+
|
|
5
|
+
export const DeployCommand = () => {
|
|
6
|
+
return new Command(`deploy`)
|
|
7
|
+
.argument(`[instanceId]`, `Instance name`, savedInstanceId())
|
|
8
|
+
.description(`Deploy to remote`)
|
|
9
|
+
.option(`-v, --verbose`, `Verbose output`)
|
|
10
|
+
.option(
|
|
11
|
+
'-i, --include <include...>',
|
|
12
|
+
'Files to include in the sync',
|
|
13
|
+
(val, prev) => [...prev, val],
|
|
14
|
+
DEFAULT_INCLUDES
|
|
15
|
+
)
|
|
16
|
+
.option(
|
|
17
|
+
'-e, --exclude <exclude...>',
|
|
18
|
+
'Files to exclude from the sync',
|
|
19
|
+
(val, prev) => [...prev, val],
|
|
20
|
+
DEFAULT_EXCLUDES
|
|
21
|
+
)
|
|
22
|
+
.action((instanceId, options) => {
|
|
23
|
+
const { include, exclude, verbose } = options
|
|
24
|
+
deployMyCode(instanceId, include, exclude, verbose)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
@@ -7,9 +7,86 @@ import { ensureDirSync } from 'fs-extra'
|
|
|
7
7
|
import multimatch from 'multimatch'
|
|
8
8
|
import { config } from '../lib/config'
|
|
9
9
|
import { getInstanceBySubdomainCnameOrId } from '../lib/getClient'
|
|
10
|
-
import {
|
|
10
|
+
import { savedInstanceId } from './../lib/defaultInstanceId'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
export const DEFAULT_INCLUDES = [
|
|
13
|
+
`pb_*`,
|
|
14
|
+
'pb_*/**/*',
|
|
15
|
+
`package.json`,
|
|
16
|
+
`bun.lockb`,
|
|
17
|
+
`patches`,
|
|
18
|
+
`patches/**/*`,
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_EXCLUDES = [`pb_data`, `pb_data/**/*`]
|
|
22
|
+
|
|
23
|
+
export type DeployOptions = {
|
|
24
|
+
include: string[]
|
|
25
|
+
exclude: string[]
|
|
26
|
+
verbose: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const watchAndDeploy = async (
|
|
30
|
+
_instanceId: string,
|
|
31
|
+
options: DeployOptions = {
|
|
32
|
+
include: DEFAULT_INCLUDES,
|
|
33
|
+
exclude: DEFAULT_EXCLUDES,
|
|
34
|
+
verbose: false,
|
|
35
|
+
}
|
|
36
|
+
) => {
|
|
37
|
+
if (!_instanceId) {
|
|
38
|
+
console.error(
|
|
39
|
+
`No instance name provided and none was found in package.json or pockethost.json. Use 'phio link <instance>'`
|
|
40
|
+
)
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
console.log(`Dev mode`)
|
|
44
|
+
const { include, exclude, verbose } = options
|
|
45
|
+
// console.log({ include, exclude })
|
|
46
|
+
|
|
47
|
+
const instance = await getInstanceBySubdomainCnameOrId(_instanceId)
|
|
48
|
+
|
|
49
|
+
const limiter = new Bottleneck({ maxConcurrent: 1 })
|
|
50
|
+
const upload = debounce(
|
|
51
|
+
() =>
|
|
52
|
+
limiter.schedule(() =>
|
|
53
|
+
deployMyCode(instance.subdomain, include, exclude, verbose).catch(
|
|
54
|
+
console.error
|
|
55
|
+
)
|
|
56
|
+
),
|
|
57
|
+
200
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const watcher = watch('.', {
|
|
61
|
+
persistent: true,
|
|
62
|
+
ignored: (file) => {
|
|
63
|
+
if (file === '.') return false
|
|
64
|
+
const isIncluded = multimatch([file], include).length > 0
|
|
65
|
+
const isExcluded = multimatch([file], exclude).length > 0
|
|
66
|
+
const isIgnored = !isIncluded || isExcluded
|
|
67
|
+
// console.log({
|
|
68
|
+
// file,
|
|
69
|
+
// include,
|
|
70
|
+
// isIncluded,
|
|
71
|
+
// exclude,
|
|
72
|
+
// isExcluded,
|
|
73
|
+
// isIgnored,
|
|
74
|
+
// })
|
|
75
|
+
return isIgnored
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
console.log(
|
|
79
|
+
`Watching for changes in ${include.join(', ')} and excluding ${exclude.join(', ')}`
|
|
80
|
+
)
|
|
81
|
+
const handle = (path: string, details: any) => {
|
|
82
|
+
upload()
|
|
83
|
+
// internal
|
|
84
|
+
// console.log(`Syncing ${path}`)
|
|
85
|
+
}
|
|
86
|
+
watcher.on('add', handle).on('change', handle).on('unlink', handle)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function deployMyCode(
|
|
13
90
|
instanceName: string,
|
|
14
91
|
include: string[],
|
|
15
92
|
exclude: string[],
|
|
@@ -33,77 +110,20 @@ async function deployMyCode(
|
|
|
33
110
|
|
|
34
111
|
export const DevCommand = () => {
|
|
35
112
|
return new Command('dev')
|
|
36
|
-
.argument(`[instanceId]`, `Instance name`,
|
|
113
|
+
.argument(`[instanceId]`, `Instance name`, savedInstanceId())
|
|
37
114
|
.description(`Watch for local modifications and sync to remote`)
|
|
38
115
|
.option(`-v, --verbose`, `Verbose output`)
|
|
39
116
|
.option(
|
|
40
117
|
'-i, --include <include...>',
|
|
41
118
|
'Files to include in the sync',
|
|
42
119
|
(val, prev) => [...prev, val],
|
|
43
|
-
|
|
44
|
-
`pb_*`,
|
|
45
|
-
'pb_*/**/*',
|
|
46
|
-
`package.json`,
|
|
47
|
-
`bun.lockb`,
|
|
48
|
-
`patches`,
|
|
49
|
-
`patches/**/*`,
|
|
50
|
-
]
|
|
120
|
+
DEFAULT_INCLUDES
|
|
51
121
|
)
|
|
52
122
|
.option(
|
|
53
123
|
'-e, --exclude <exclude...>',
|
|
54
124
|
'Files to exclude from the sync',
|
|
55
125
|
(val, prev) => [...prev, val],
|
|
56
|
-
|
|
126
|
+
DEFAULT_EXCLUDES
|
|
57
127
|
)
|
|
58
|
-
.action(
|
|
59
|
-
if (!_instanceId) {
|
|
60
|
-
console.error(
|
|
61
|
-
`No instance name provided and none was found in package.json or pockethost.json. Use 'phio link <instance>'`
|
|
62
|
-
)
|
|
63
|
-
process.exit(1)
|
|
64
|
-
}
|
|
65
|
-
console.log(`Dev mode`)
|
|
66
|
-
// console.log({ include, exclude })
|
|
67
|
-
|
|
68
|
-
const instance = await getInstanceBySubdomainCnameOrId(_instanceId)
|
|
69
|
-
|
|
70
|
-
const limiter = new Bottleneck({ maxConcurrent: 1 })
|
|
71
|
-
const upload = debounce(
|
|
72
|
-
() =>
|
|
73
|
-
limiter.schedule(() =>
|
|
74
|
-
deployMyCode(instance.subdomain, include, exclude, verbose).catch(
|
|
75
|
-
console.error
|
|
76
|
-
)
|
|
77
|
-
),
|
|
78
|
-
200
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
const watcher = watch('.', {
|
|
82
|
-
persistent: true,
|
|
83
|
-
ignored: (file) => {
|
|
84
|
-
if (file === '.') return false
|
|
85
|
-
const isIncluded = multimatch([file], include).length > 0
|
|
86
|
-
const isExcluded = multimatch([file], exclude).length > 0
|
|
87
|
-
const isIgnored = !isIncluded || isExcluded
|
|
88
|
-
// console.log({
|
|
89
|
-
// file,
|
|
90
|
-
// include,
|
|
91
|
-
// isIncluded,
|
|
92
|
-
// exclude,
|
|
93
|
-
// isExcluded,
|
|
94
|
-
// isIgnored,
|
|
95
|
-
// })
|
|
96
|
-
return isIgnored
|
|
97
|
-
},
|
|
98
|
-
})
|
|
99
|
-
console.log(
|
|
100
|
-
`Watching for changes in ${include.join(', ')} and excluding ${exclude.join(', ')}`
|
|
101
|
-
)
|
|
102
|
-
const handle = (path: string, details: any) => {
|
|
103
|
-
upload()
|
|
104
|
-
// internal
|
|
105
|
-
// console.log(`Syncing ${path}`)
|
|
106
|
-
}
|
|
107
|
-
watcher.on('add', handle).on('change', handle).on('unlink', handle)
|
|
108
|
-
})
|
|
128
|
+
.action(watchAndDeploy)
|
|
109
129
|
}
|
|
@@ -1,21 +1,59 @@
|
|
|
1
|
+
import { select } from '@inquirer/prompts'
|
|
1
2
|
import { Command } from 'commander'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { config } from '../lib/config'
|
|
4
|
+
import { InstanceFields } from '../lib/InstanceFields'
|
|
5
|
+
import { getClient, getInstanceBySubdomainCnameOrId } from './../lib/getClient'
|
|
6
|
+
|
|
7
|
+
export const isLinked = () => !!config('instanceId')
|
|
8
|
+
|
|
9
|
+
export const link = async (instanceNameOrId: string) => {
|
|
10
|
+
const instance = await getInstanceBySubdomainCnameOrId(instanceNameOrId)
|
|
11
|
+
if (!instance) {
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
config('instanceId', instance.subdomain)
|
|
15
|
+
return instance
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const linkWithUserInput = async () => {
|
|
19
|
+
const client = getClient()
|
|
20
|
+
const instances = await client
|
|
21
|
+
.collection(`instances`)
|
|
22
|
+
.getFullList<InstanceFields>()
|
|
23
|
+
while (true) {
|
|
24
|
+
const instanceNameOrId = await select({
|
|
25
|
+
message: `Choose the instance you'd like to link`,
|
|
26
|
+
choices: instances.map((instance) => ({
|
|
27
|
+
name: `${instance.subdomain} (${instance.id}) ${
|
|
28
|
+
instance.cname ? `(${instance.cname})` : ''
|
|
29
|
+
} (${instance.status.toUpperCase()})`,
|
|
30
|
+
value: instance.subdomain,
|
|
31
|
+
})),
|
|
32
|
+
})
|
|
33
|
+
const instance = await link(instanceNameOrId)
|
|
34
|
+
if (!instance) {
|
|
35
|
+
console.error(`Instance not found`)
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
console.log(`Linked ${instance.subdomain}`)
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
}
|
|
4
42
|
|
|
5
43
|
export const LinkCommand = () => {
|
|
6
44
|
return new Command(`link`)
|
|
7
|
-
.argument(
|
|
45
|
+
.argument(`[instance]`, `Instance name or ID`)
|
|
8
46
|
.description(`Link a local directory to a remote instance`)
|
|
9
47
|
.action(async (_anyName) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
48
|
+
if (_anyName) {
|
|
49
|
+
const instance = await link(_anyName)
|
|
50
|
+
if (!instance) {
|
|
51
|
+
console.error(`Instance ${_anyName} not found`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
console.log(`Linked ${instance.subdomain}`)
|
|
55
|
+
return
|
|
14
56
|
}
|
|
15
|
-
|
|
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 })
|
|
57
|
+
await linkWithUserInput()
|
|
20
58
|
})
|
|
21
59
|
}
|
|
@@ -5,53 +5,55 @@ import { config } from '../lib/config'
|
|
|
5
5
|
import { getClient } from './../lib/getClient'
|
|
6
6
|
import { runTasks } from './../lib/Task'
|
|
7
7
|
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
return true
|
|
22
|
-
},
|
|
23
|
-
})
|
|
8
|
+
export const loginWithUserInput = async () => {
|
|
9
|
+
while (true) {
|
|
10
|
+
const email = await input({
|
|
11
|
+
message: 'Enter your pockethost.io email address',
|
|
12
|
+
default: config('email'),
|
|
13
|
+
validate: (input: string) => {
|
|
14
|
+
if (!EmailValidator.validate(input)) {
|
|
15
|
+
return 'Invalid email address'
|
|
16
|
+
}
|
|
17
|
+
return true
|
|
18
|
+
},
|
|
19
|
+
})
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const pw = await password({
|
|
22
|
+
message: 'Enter your pockethost.io password',
|
|
23
|
+
})
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
config(`email`, email)
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
const client = getClient()
|
|
28
|
+
try {
|
|
29
|
+
await runTasks([
|
|
30
|
+
{
|
|
31
|
+
name: `Logging in`,
|
|
32
|
+
run: async () => {
|
|
33
|
+
const res = await client
|
|
34
|
+
.collection('users')
|
|
35
|
+
.authWithPassword(email, pw)
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
])
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error(
|
|
41
|
+
`There was an error logging in. Please try again or go to https://pockethost.io to reset your password.`
|
|
42
|
+
)
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
54
|
-
break
|
|
55
|
-
}
|
|
56
|
-
console.log(`Logged in!`)
|
|
46
|
+
config(`auth`, {
|
|
47
|
+
token: client.authStore.exportToCookie(),
|
|
48
|
+
record: client.authStore.model,
|
|
57
49
|
})
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
console.log(`Logged in!`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const LoginCommand = () =>
|
|
56
|
+
new Command('login')
|
|
57
|
+
.description(`Log in to PocketHost`)
|
|
58
|
+
.helpOption(false)
|
|
59
|
+
.action(loginWithUserInput)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fetchEventSource } from '@sentool/fetch-event-source'
|
|
2
2
|
import { Command } from 'commander'
|
|
3
3
|
import { config } from '../lib/config'
|
|
4
|
-
import {
|
|
4
|
+
import { savedInstanceId } from '../lib/defaultInstanceId'
|
|
5
5
|
|
|
6
6
|
export enum StreamNames {
|
|
7
7
|
StdOut = 'stdout',
|
|
@@ -64,7 +64,7 @@ const watchInstanceLog = (
|
|
|
64
64
|
export const LogsCommand = () => {
|
|
65
65
|
return new Command('logs')
|
|
66
66
|
.description(`Tail instance logs`)
|
|
67
|
-
.argument('[instance]', 'Instance ID',
|
|
67
|
+
.argument('[instance]', 'Instance ID', savedInstanceId())
|
|
68
68
|
.action((instance) => {
|
|
69
69
|
watchInstanceLog(instance, (log) => {
|
|
70
70
|
const { time, message, stream } = log
|
package/src/index.ts
CHANGED
|
@@ -1,22 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import { program } from 'commander'
|
|
3
|
-
import { version } from '../package.json'
|
|
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'
|
|
1
|
+
import { config } from './lib/config'
|
|
10
2
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.description(`CLI access to phio`)
|
|
15
|
-
.addCommand(LoginCommand())
|
|
16
|
-
.addCommand(LogsCommand())
|
|
17
|
-
.addCommand(DevCommand())
|
|
18
|
-
.addCommand(WhoAmICommand())
|
|
19
|
-
.addCommand(ListCommand())
|
|
20
|
-
.addCommand(LinkCommand())
|
|
3
|
+
export const isLoggedIn = () => {
|
|
4
|
+
return !!config('auth')
|
|
5
|
+
}
|
|
21
6
|
|
|
22
|
-
|
|
7
|
+
export * from './commands/DevCommand'
|
|
8
|
+
export * from './commands/LinkCommand'
|
|
9
|
+
export * from './commands/LoginCommand'
|
|
10
|
+
export { config } from './lib/config'
|
|
11
|
+
export { getClient } from './lib/getClient'
|
package/src/lib/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'fs'
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const savedInstanceId = () => {
|
|
4
4
|
if (existsSync('package.json')) {
|
|
5
5
|
const pkg = JSON.parse(readFileSync('package.json').toString())
|
|
6
6
|
if (pkg.pockethost?.instanceId) {
|