bazilion 0.0.0 → 0.1.1
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 +53 -152
- package/dist/cli.js +2730 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.js +6460 -0
- package/dist/daemon.js.map +1 -0
- package/dist/worker.js +2176 -0
- package/dist/worker.js.map +1 -0
- package/package.json +57 -28
- package/.understand-anything/.understandignore +0 -25
- package/.understand-anything/fingerprints.json +0 -14267
- package/.understand-anything/knowledge-graph.json +0 -18128
- package/.understand-anything/meta.json +0 -6
- package/CLAUDE.md +0 -164
- package/apps/cli/package.json +0 -21
- package/apps/cli/src/auth-file.ts +0 -18
- package/apps/cli/src/client.ts +0 -37
- package/apps/cli/src/columnize.ts +0 -32
- package/apps/cli/src/commands/agent.ts +0 -574
- package/apps/cli/src/commands/auth.ts +0 -110
- package/apps/cli/src/commands/backup.ts +0 -135
- package/apps/cli/src/commands/completion.ts +0 -155
- package/apps/cli/src/commands/config.ts +0 -95
- package/apps/cli/src/commands/doctor.ts +0 -131
- package/apps/cli/src/commands/group.ts +0 -132
- package/apps/cli/src/commands/inbox.ts +0 -82
- package/apps/cli/src/commands/login.ts +0 -73
- package/apps/cli/src/commands/memory.ts +0 -106
- package/apps/cli/src/commands/profile.ts +0 -259
- package/apps/cli/src/commands/provider.ts +0 -170
- package/apps/cli/src/commands/send.ts +0 -24
- package/apps/cli/src/commands/serve.ts +0 -89
- package/apps/cli/src/commands/skill.ts +0 -120
- package/apps/cli/src/commands/token.ts +0 -148
- package/apps/cli/src/commands/trigger.ts +0 -129
- package/apps/cli/src/commands/uninstall.ts +0 -156
- package/apps/cli/src/index.ts +0 -196
- package/apps/cli/src/paths.ts +0 -12
- package/apps/cli/test/agent.test.ts +0 -213
- package/apps/cli/test/backup.test.ts +0 -95
- package/apps/cli/test/chat.test.ts +0 -539
- package/apps/cli/test/columnize.test.ts +0 -29
- package/apps/cli/test/completion.test.ts +0 -45
- package/apps/cli/test/config-page.test.ts +0 -151
- package/apps/cli/test/group.test.ts +0 -74
- package/apps/cli/test/helpers.ts +0 -70
- package/apps/cli/test/inbox-autodeliver.test.ts +0 -143
- package/apps/cli/test/inbox.test.ts +0 -147
- package/apps/cli/test/memory.test.ts +0 -69
- package/apps/cli/test/profile.test.ts +0 -110
- package/apps/cli/test/send.test.ts +0 -30
- package/apps/cli/test/server-fixture.ts +0 -212
- package/apps/cli/test/session-head.test.ts +0 -45
- package/apps/cli/test/skill.test.ts +0 -128
- package/apps/cli/test/token.test.ts +0 -109
- package/apps/cli/test/trigger.test.ts +0 -245
- package/apps/cli/tsconfig.json +0 -4
- package/apps/daemon/package.json +0 -31
- package/apps/daemon/src/app.ts +0 -41
- package/apps/daemon/src/core/agent/archive.ts +0 -8
- package/apps/daemon/src/core/agent/delete.ts +0 -30
- package/apps/daemon/src/core/agent/resolve.ts +0 -31
- package/apps/daemon/src/core/agent/spawn.ts +0 -95
- package/apps/daemon/src/core/agent/unarchive.ts +0 -11
- package/apps/daemon/src/core/availableModels.ts +0 -58
- package/apps/daemon/src/core/db/client.ts +0 -113
- package/apps/daemon/src/core/db/migrate.ts +0 -41
- package/apps/daemon/src/core/db/migrations/0001_init.sql +0 -179
- package/apps/daemon/src/core/group/delete.ts +0 -21
- package/apps/daemon/src/core/group/register.ts +0 -68
- package/apps/daemon/src/core/index.ts +0 -71
- package/apps/daemon/src/core/paths.ts +0 -56
- package/apps/daemon/src/core/profile/create.ts +0 -78
- package/apps/daemon/src/core/profile/delete.ts +0 -26
- package/apps/daemon/src/core/profile/identity.ts +0 -70
- package/apps/daemon/src/core/profile/load.ts +0 -45
- package/apps/daemon/src/core/profile/seed.ts +0 -74
- package/apps/daemon/src/core/profile/templates.ts +0 -60
- package/apps/daemon/src/core/profile/update.ts +0 -57
- package/apps/daemon/src/core/profile/validate.ts +0 -9
- package/apps/daemon/src/core/repos/agents.ts +0 -202
- package/apps/daemon/src/core/repos/config.ts +0 -78
- package/apps/daemon/src/core/repos/groups.ts +0 -55
- package/apps/daemon/src/core/repos/messages.ts +0 -127
- package/apps/daemon/src/core/repos/profiles.ts +0 -83
- package/apps/daemon/src/core/repos/providerModels.ts +0 -58
- package/apps/daemon/src/core/repos/providerState.ts +0 -37
- package/apps/daemon/src/core/repos/secrets.ts +0 -145
- package/apps/daemon/src/core/repos/skillMeta.ts +0 -49
- package/apps/daemon/src/core/repos/triggers.ts +0 -101
- package/apps/daemon/src/core/repos/webTokens.ts +0 -87
- package/apps/daemon/src/core/secrets.ts +0 -65
- package/apps/daemon/src/core/services.ts +0 -264
- package/apps/daemon/src/core/skills/discover.ts +0 -28
- package/apps/daemon/src/core/skills/import.ts +0 -136
- package/apps/daemon/src/core/skills/parse.ts +0 -52
- package/apps/daemon/src/core/skills/resolve.ts +0 -50
- package/apps/daemon/src/index.ts +0 -45
- package/apps/daemon/src/lib/agent-cancel.ts +0 -48
- package/apps/daemon/src/lib/agent-id.ts +0 -13
- package/apps/daemon/src/lib/agent-turn.ts +0 -56
- package/apps/daemon/src/lib/api-key.ts +0 -56
- package/apps/daemon/src/lib/auth.ts +0 -41
- package/apps/daemon/src/lib/cron.ts +0 -93
- package/apps/daemon/src/lib/ctx.ts +0 -80
- package/apps/daemon/src/lib/messaging-host.ts +0 -34
- package/apps/daemon/src/lib/middleware-auth.ts +0 -52
- package/apps/daemon/src/lib/scheduler.ts +0 -294
- package/apps/daemon/src/routes/agents.ts +0 -772
- package/apps/daemon/src/routes/auth-login.ts +0 -193
- package/apps/daemon/src/routes/config.ts +0 -267
- package/apps/daemon/src/routes/groups.ts +0 -133
- package/apps/daemon/src/routes/messages.ts +0 -29
- package/apps/daemon/src/routes/misc.ts +0 -239
- package/apps/daemon/src/routes/profiles.ts +0 -197
- package/apps/daemon/src/routes/skills.ts +0 -123
- package/apps/daemon/src/routes/triggers.ts +0 -29
- package/apps/daemon/src/runtime/auth/openai-codex.ts +0 -121
- package/apps/daemon/src/runtime/auto-reply/heartbeat.ts +0 -31
- package/apps/daemon/src/runtime/index.ts +0 -77
- package/apps/daemon/src/runtime/memory/files.ts +0 -103
- package/apps/daemon/src/runtime/memory/qmd.ts +0 -152
- package/apps/daemon/src/runtime/memory/types.ts +0 -16
- package/apps/daemon/src/runtime/pi/events.ts +0 -173
- package/apps/daemon/src/runtime/pi/session.ts +0 -536
- package/apps/daemon/src/runtime/pi/tools.ts +0 -85
- package/apps/daemon/src/runtime/providers/catalog.ts +0 -145
- package/apps/daemon/src/runtime/providers/pi-adapter.ts +0 -272
- package/apps/daemon/src/runtime/providers/registry.ts +0 -374
- package/apps/daemon/src/runtime/providers/retry.ts +0 -176
- package/apps/daemon/src/runtime/providers/types.ts +0 -33
- package/apps/daemon/src/runtime/session/prompt.ts +0 -83
- package/apps/daemon/src/runtime/tools/bootstrap.ts +0 -22
- package/apps/daemon/src/runtime/tools/home.ts +0 -114
- package/apps/daemon/src/runtime/tools/memory.ts +0 -81
- package/apps/daemon/src/runtime/tools/messaging.ts +0 -127
- package/apps/daemon/src/runtime/tools/registry.ts +0 -29
- package/apps/daemon/src/runtime/tools/types.ts +0 -13
- package/apps/daemon/src/runtime/tools/web-extract.ts +0 -110
- package/apps/daemon/src/runtime/tools/web-ssrf.ts +0 -245
- package/apps/daemon/src/runtime/tools/web.ts +0 -273
- package/apps/daemon/src/runtime/worker/entry.ts +0 -221
- package/apps/daemon/src/runtime/worker/ipc-protocol.ts +0 -75
- package/apps/daemon/src/runtime/worker/spawn.ts +0 -249
- package/apps/daemon/test/core/agents.test.ts +0 -319
- package/apps/daemon/test/core/available-models.test.ts +0 -63
- package/apps/daemon/test/core/config.test.ts +0 -99
- package/apps/daemon/test/core/groups.test.ts +0 -63
- package/apps/daemon/test/core/helpers.ts +0 -50
- package/apps/daemon/test/core/identity.test.ts +0 -85
- package/apps/daemon/test/core/migrations.test.ts +0 -83
- package/apps/daemon/test/core/profiles.test.ts +0 -182
- package/apps/daemon/test/core/provider-models.test.ts +0 -57
- package/apps/daemon/test/core/provider-state.test.ts +0 -36
- package/apps/daemon/test/core/skill-meta.test.ts +0 -45
- package/apps/daemon/test/core/skills.test.ts +0 -271
- package/apps/daemon/test/core/triggers.test.ts +0 -182
- package/apps/daemon/test/core/web-tokens.test.ts +0 -79
- package/apps/daemon/test/cron.test.ts +0 -90
- package/apps/daemon/test/runtime/heartbeat.test.ts +0 -34
- package/apps/daemon/test/runtime/memory-qmd.test.ts +0 -97
- package/apps/daemon/test/runtime/memory.test.ts +0 -78
- package/apps/daemon/test/runtime/messaging.test.ts +0 -213
- package/apps/daemon/test/runtime/mock-server.ts +0 -65
- package/apps/daemon/test/runtime/openai-codex-auth.test.ts +0 -116
- package/apps/daemon/test/runtime/providers.test.ts +0 -306
- package/apps/daemon/test/runtime/retry.test.ts +0 -191
- package/apps/daemon/test/runtime/session-head.test.ts +0 -90
- package/apps/daemon/test/runtime/tools-home.test.ts +0 -102
- package/apps/daemon/test/runtime/tools-web.test.ts +0 -206
- package/apps/daemon/tsconfig.json +0 -4
- package/apps/mobile/README.md +0 -60
- package/apps/mobile/app/_layout.tsx +0 -58
- package/apps/mobile/app/agents/[id]/chat.tsx +0 -486
- package/apps/mobile/app/agents/[id]/index.tsx +0 -166
- package/apps/mobile/app/agents/index.tsx +0 -212
- package/apps/mobile/app/index.tsx +0 -21
- package/apps/mobile/app/pair.tsx +0 -226
- package/apps/mobile/app/settings.tsx +0 -419
- package/apps/mobile/app.json +0 -49
- package/apps/mobile/assets/adaptive-icon.png +0 -0
- package/apps/mobile/assets/favicon.png +0 -0
- package/apps/mobile/assets/icon.png +0 -0
- package/apps/mobile/assets/splash-icon.png +0 -0
- package/apps/mobile/babel.config.js +0 -6
- package/apps/mobile/metro.config.js +0 -28
- package/apps/mobile/package.json +0 -44
- package/apps/mobile/src/auth.ts +0 -66
- package/apps/mobile/src/pair-url.ts +0 -48
- package/apps/mobile/src/theme-context.tsx +0 -88
- package/apps/mobile/src/theme.ts +0 -135
- package/apps/mobile/test/pair-url.test.ts +0 -46
- package/apps/mobile/tsconfig.json +0 -23
- package/apps/web/components.json +0 -25
- package/apps/web/package.json +0 -39
- package/apps/web/public/baziu.svg +0 -8
- package/apps/web/src/components/AgentTabs.tsx +0 -45
- package/apps/web/src/components/BaziuLogo.tsx +0 -21
- package/apps/web/src/components/ChatPane.tsx +0 -1033
- package/apps/web/src/components/ConfigTabs.tsx +0 -29
- package/apps/web/src/components/CopyButton.tsx +0 -68
- package/apps/web/src/components/CreateGroupDialog.tsx +0 -127
- package/apps/web/src/components/FieldRow.tsx +0 -94
- package/apps/web/src/components/Footer.tsx +0 -10
- package/apps/web/src/components/PawIcon.tsx +0 -15
- package/apps/web/src/components/Sidebar.tsx +0 -287
- package/apps/web/src/components/SpawnDialog.tsx +0 -129
- package/apps/web/src/components/ThemeToggle.tsx +0 -75
- package/apps/web/src/components/TopNav.tsx +0 -34
- package/apps/web/src/components/ui/button.tsx +0 -67
- package/apps/web/src/components/ui/card.tsx +0 -103
- package/apps/web/src/components/ui/checkbox.tsx +0 -31
- package/apps/web/src/components/ui/dialog.tsx +0 -168
- package/apps/web/src/components/ui/input.tsx +0 -19
- package/apps/web/src/components/ui/label.tsx +0 -22
- package/apps/web/src/components/ui/radio-group.tsx +0 -44
- package/apps/web/src/components/ui/select.tsx +0 -192
- package/apps/web/src/components/ui/separator.tsx +0 -26
- package/apps/web/src/components/ui/table.tsx +0 -116
- package/apps/web/src/components/ui/tabs.tsx +0 -88
- package/apps/web/src/components/ui/textarea.tsx +0 -18
- package/apps/web/src/lib/auth.ts +0 -50
- package/apps/web/src/lib/daemon-client.ts +0 -34
- package/apps/web/src/lib/md.ts +0 -45
- package/apps/web/src/lib/utils.ts +0 -6
- package/apps/web/src/lib/wire-constants.ts +0 -27
- package/apps/web/src/routeTree.gen.ts +0 -408
- package/apps/web/src/router.tsx +0 -20
- package/apps/web/src/routes/__root.tsx +0 -123
- package/apps/web/src/routes/agents/$id/inbox.tsx +0 -207
- package/apps/web/src/routes/agents/$id/index.tsx +0 -527
- package/apps/web/src/routes/agents/$id/triggers.tsx +0 -239
- package/apps/web/src/routes/agents/index.tsx +0 -265
- package/apps/web/src/routes/api/$.ts +0 -88
- package/apps/web/src/routes/config/index.tsx +0 -315
- package/apps/web/src/routes/config/services.tsx +0 -49
- package/apps/web/src/routes/config/tokens.tsx +0 -192
- package/apps/web/src/routes/groups/$id/index.tsx +0 -153
- package/apps/web/src/routes/groups/$id/memory.tsx +0 -321
- package/apps/web/src/routes/groups/index.tsx +0 -191
- package/apps/web/src/routes/index.tsx +0 -133
- package/apps/web/src/routes/login.tsx +0 -63
- package/apps/web/src/routes/profiles/$id.tsx +0 -549
- package/apps/web/src/routes/profiles/index.tsx +0 -458
- package/apps/web/src/routes/skills/index.tsx +0 -297
- package/apps/web/src/routes/welcome.tsx +0 -61
- package/apps/web/src/styles.css +0 -449
- package/apps/web/tsconfig.json +0 -25
- package/apps/web/vite.config.ts +0 -25
- package/biome.json +0 -23
- package/docs/agent-engine.md +0 -219
- package/docs/architecture.md +0 -627
- package/docs/backlog/README.md +0 -42
- package/docs/backlog/draft/BAZ-001-a2a-federation-spike.md +0 -125
- package/docs/openclaw-reference.md +0 -210
- package/packages/api-types/package.json +0 -11
- package/packages/api-types/src/entities.ts +0 -146
- package/packages/api-types/src/events.ts +0 -55
- package/packages/api-types/src/index.ts +0 -488
- package/packages/api-types/src/memory.ts +0 -15
- package/packages/client/package.json +0 -13
- package/packages/client/src/index.ts +0 -117
- package/pnpm-workspace.yaml +0 -3
- package/tsconfig.base.json +0 -23
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -22
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import type { CreateTokenResponse, ListTokensResponse, WebToken } from '@bazilion/api-types'
|
|
2
|
-
import { ApiClientError } from '@bazilion/client'
|
|
3
|
-
import { router } from 'expo-router'
|
|
4
|
-
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
5
|
-
import {
|
|
6
|
-
ActivityIndicator,
|
|
7
|
-
Alert,
|
|
8
|
-
FlatList,
|
|
9
|
-
Pressable,
|
|
10
|
-
ScrollView,
|
|
11
|
-
StyleSheet,
|
|
12
|
-
Text,
|
|
13
|
-
TextInput,
|
|
14
|
-
View,
|
|
15
|
-
} from 'react-native'
|
|
16
|
-
import { clearCredentials, clientFor, type Credentials, loadCredentials } from '@/src/auth'
|
|
17
|
-
import { useColors, useThemeMode } from '@/src/theme-context'
|
|
18
|
-
import { type Colors, fonts, radii, type ThemeMode } from '@/src/theme'
|
|
19
|
-
|
|
20
|
-
type Load =
|
|
21
|
-
| { kind: 'loading' }
|
|
22
|
-
| { kind: 'ready'; creds: Credentials; tokens: WebToken[]; bootstrapId: string | null }
|
|
23
|
-
| { kind: 'error'; message: string }
|
|
24
|
-
|
|
25
|
-
export default function Settings() {
|
|
26
|
-
const [load, setLoad] = useState<Load>({ kind: 'loading' })
|
|
27
|
-
const [newLabel, setNewLabel] = useState('')
|
|
28
|
-
const [minting, setMinting] = useState(false)
|
|
29
|
-
const [mintedSecret, setMintedSecret] = useState<{ token: string; meta: WebToken } | null>(null)
|
|
30
|
-
const colors = useColors()
|
|
31
|
-
const styles = useMemo(() => makeStyles(colors), [colors])
|
|
32
|
-
|
|
33
|
-
const fetchTokens = useCallback(async () => {
|
|
34
|
-
setLoad({ kind: 'loading' })
|
|
35
|
-
const creds = await loadCredentials()
|
|
36
|
-
if (!creds) {
|
|
37
|
-
router.replace('/pair')
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const res = await clientFor(creds).get<ListTokensResponse>('/api/tokens')
|
|
42
|
-
// Identify the bootstrap row so the UI can hide its revoke button
|
|
43
|
-
// (matches the daemon's 409-on-revoke guard).
|
|
44
|
-
const bootstrap = res.tokens.find((t) => t.label === 'bootstrap')
|
|
45
|
-
setLoad({
|
|
46
|
-
kind: 'ready',
|
|
47
|
-
creds,
|
|
48
|
-
tokens: res.tokens,
|
|
49
|
-
bootstrapId: bootstrap?.id ?? null,
|
|
50
|
-
})
|
|
51
|
-
} catch (err) {
|
|
52
|
-
if (err instanceof ApiClientError && err.status === 401) {
|
|
53
|
-
await clearCredentials()
|
|
54
|
-
router.replace('/pair')
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
const message = err instanceof Error ? err.message : 'unknown error'
|
|
58
|
-
setLoad({ kind: 'error', message })
|
|
59
|
-
}
|
|
60
|
-
}, [])
|
|
61
|
-
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
fetchTokens()
|
|
64
|
-
}, [fetchTokens])
|
|
65
|
-
|
|
66
|
-
const onMint = useCallback(async () => {
|
|
67
|
-
if (load.kind !== 'ready' || minting) return
|
|
68
|
-
const label = newLabel.trim()
|
|
69
|
-
if (!label) return
|
|
70
|
-
setMinting(true)
|
|
71
|
-
try {
|
|
72
|
-
const res = await clientFor(load.creds).post<CreateTokenResponse>('/api/tokens', { label })
|
|
73
|
-
setMintedSecret({ token: res.token, meta: res.meta })
|
|
74
|
-
setNewLabel('')
|
|
75
|
-
await fetchTokens()
|
|
76
|
-
} catch (err) {
|
|
77
|
-
const message = err instanceof Error ? err.message : 'unknown error'
|
|
78
|
-
Alert.alert('Could not mint token', message)
|
|
79
|
-
} finally {
|
|
80
|
-
setMinting(false)
|
|
81
|
-
}
|
|
82
|
-
}, [load, minting, newLabel, fetchTokens])
|
|
83
|
-
|
|
84
|
-
const onRevoke = useCallback(
|
|
85
|
-
async (id: string) => {
|
|
86
|
-
if (load.kind !== 'ready') return
|
|
87
|
-
Alert.alert(
|
|
88
|
-
'Revoke token?',
|
|
89
|
-
'Devices using this token will lose access immediately.',
|
|
90
|
-
[
|
|
91
|
-
{ text: 'Cancel', style: 'cancel' },
|
|
92
|
-
{
|
|
93
|
-
text: 'Revoke',
|
|
94
|
-
style: 'destructive',
|
|
95
|
-
onPress: async () => {
|
|
96
|
-
try {
|
|
97
|
-
await clientFor(load.creds).del(`/api/tokens/${id}`)
|
|
98
|
-
await fetchTokens()
|
|
99
|
-
} catch (err) {
|
|
100
|
-
const message = err instanceof Error ? err.message : 'unknown error'
|
|
101
|
-
Alert.alert('Could not revoke', message)
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
{ cancelable: true },
|
|
107
|
-
)
|
|
108
|
-
},
|
|
109
|
-
[load, fetchTokens],
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
const onUnpair = useCallback(() => {
|
|
113
|
-
Alert.alert(
|
|
114
|
-
'Unpair this device?',
|
|
115
|
-
'You will need to scan a new pairing QR to reconnect.',
|
|
116
|
-
[
|
|
117
|
-
{ text: 'Cancel', style: 'cancel' },
|
|
118
|
-
{
|
|
119
|
-
text: 'Unpair',
|
|
120
|
-
style: 'destructive',
|
|
121
|
-
onPress: async () => {
|
|
122
|
-
await clearCredentials()
|
|
123
|
-
router.replace('/pair')
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
{ cancelable: true },
|
|
128
|
-
)
|
|
129
|
-
}, [])
|
|
130
|
-
|
|
131
|
-
if (load.kind === 'loading') {
|
|
132
|
-
return (
|
|
133
|
-
<View style={styles.centered}>
|
|
134
|
-
<ActivityIndicator />
|
|
135
|
-
</View>
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (load.kind === 'error') {
|
|
140
|
-
return (
|
|
141
|
-
<View style={styles.centered}>
|
|
142
|
-
<Text style={styles.errorTitle}>Couldn't load settings</Text>
|
|
143
|
-
<Text style={styles.errorBody}>{load.message}</Text>
|
|
144
|
-
<Pressable style={styles.primaryBtn} onPress={fetchTokens}>
|
|
145
|
-
<Text style={styles.primaryBtnText}>Retry</Text>
|
|
146
|
-
</Pressable>
|
|
147
|
-
<Pressable style={styles.linkBtn} onPress={onUnpair}>
|
|
148
|
-
<Text style={styles.linkBtnText}>Unpair</Text>
|
|
149
|
-
</Pressable>
|
|
150
|
-
</View>
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const tokenPreview = `${load.creds.token.slice(0, 6)}…${load.creds.token.slice(-4)}`
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
<ScrollView style={styles.screen} contentContainerStyle={styles.content}>
|
|
158
|
-
<Section label="server">
|
|
159
|
-
<Text style={styles.mono}>{load.creds.server}</Text>
|
|
160
|
-
</Section>
|
|
161
|
-
|
|
162
|
-
<Section label="this device's token">
|
|
163
|
-
<Text style={styles.mono}>{tokenPreview}</Text>
|
|
164
|
-
</Section>
|
|
165
|
-
|
|
166
|
-
<View style={styles.divider} />
|
|
167
|
-
|
|
168
|
-
<Section label="theme">
|
|
169
|
-
<ThemePicker />
|
|
170
|
-
</Section>
|
|
171
|
-
|
|
172
|
-
<View style={styles.divider} />
|
|
173
|
-
|
|
174
|
-
<Section label="mint a new token">
|
|
175
|
-
<View style={styles.mintRow}>
|
|
176
|
-
<TextInput
|
|
177
|
-
value={newLabel}
|
|
178
|
-
onChangeText={setNewLabel}
|
|
179
|
-
placeholder="e.g. tablet, laptop"
|
|
180
|
-
placeholderTextColor="#aaa"
|
|
181
|
-
style={styles.mintInput}
|
|
182
|
-
editable={!minting}
|
|
183
|
-
autoCapitalize="none"
|
|
184
|
-
/>
|
|
185
|
-
<Pressable
|
|
186
|
-
onPress={onMint}
|
|
187
|
-
disabled={minting || !newLabel.trim()}
|
|
188
|
-
style={({ pressed }) => [
|
|
189
|
-
styles.mintBtn,
|
|
190
|
-
(minting || !newLabel.trim()) && styles.mintBtnDisabled,
|
|
191
|
-
pressed && styles.mintBtnPressed,
|
|
192
|
-
]}
|
|
193
|
-
>
|
|
194
|
-
<Text style={styles.mintBtnText}>{minting ? '…' : 'Mint'}</Text>
|
|
195
|
-
</Pressable>
|
|
196
|
-
</View>
|
|
197
|
-
{mintedSecret ? (
|
|
198
|
-
<View style={styles.mintedBox}>
|
|
199
|
-
<Text style={styles.mintedLabel}>new token (shown once):</Text>
|
|
200
|
-
<Text style={styles.mintedToken} selectable>
|
|
201
|
-
{mintedSecret.token}
|
|
202
|
-
</Text>
|
|
203
|
-
<Text style={styles.mintedHint}>
|
|
204
|
-
copy this now — it cannot be retrieved later. label: {mintedSecret.meta.label}
|
|
205
|
-
</Text>
|
|
206
|
-
</View>
|
|
207
|
-
) : null}
|
|
208
|
-
</Section>
|
|
209
|
-
|
|
210
|
-
<Section label={`tokens (${load.tokens.length})`}>
|
|
211
|
-
<FlatList
|
|
212
|
-
data={load.tokens}
|
|
213
|
-
scrollEnabled={false}
|
|
214
|
-
keyExtractor={(t) => t.id}
|
|
215
|
-
renderItem={({ item }) => (
|
|
216
|
-
<TokenRow
|
|
217
|
-
token={item}
|
|
218
|
-
isBootstrap={item.id === load.bootstrapId}
|
|
219
|
-
onRevoke={() => onRevoke(item.id)}
|
|
220
|
-
/>
|
|
221
|
-
)}
|
|
222
|
-
ItemSeparatorComponent={() => <View style={styles.tokenSep} />}
|
|
223
|
-
/>
|
|
224
|
-
</Section>
|
|
225
|
-
|
|
226
|
-
<View style={styles.divider} />
|
|
227
|
-
|
|
228
|
-
<Pressable
|
|
229
|
-
onPress={onUnpair}
|
|
230
|
-
style={({ pressed }) => [styles.dangerBtn, pressed && styles.dangerBtnPressed]}
|
|
231
|
-
>
|
|
232
|
-
<Text style={styles.dangerBtnText}>Unpair this device</Text>
|
|
233
|
-
</Pressable>
|
|
234
|
-
</ScrollView>
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function Section({ label, children }: { label: string; children: React.ReactNode }) {
|
|
239
|
-
const colors = useColors()
|
|
240
|
-
const styles = useMemo(() => makeStyles(colors), [colors])
|
|
241
|
-
return (
|
|
242
|
-
<View style={styles.section}>
|
|
243
|
-
<Text style={styles.sectionLabel}>{label}</Text>
|
|
244
|
-
<View style={styles.sectionBody}>{children}</View>
|
|
245
|
-
</View>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function TokenRow({
|
|
250
|
-
token,
|
|
251
|
-
isBootstrap,
|
|
252
|
-
onRevoke,
|
|
253
|
-
}: {
|
|
254
|
-
token: WebToken
|
|
255
|
-
isBootstrap: boolean
|
|
256
|
-
onRevoke: () => void
|
|
257
|
-
}) {
|
|
258
|
-
const colors = useColors()
|
|
259
|
-
const styles = useMemo(() => makeStyles(colors), [colors])
|
|
260
|
-
const status = token.revokedAt ? 'revoked' : 'active'
|
|
261
|
-
const last = token.lastUsedAt ? new Date(token.lastUsedAt).toLocaleString() : '(never used)'
|
|
262
|
-
return (
|
|
263
|
-
<View style={styles.tokenRow}>
|
|
264
|
-
<View style={styles.tokenInfo}>
|
|
265
|
-
<Text style={styles.tokenLabel}>
|
|
266
|
-
{token.label} {isBootstrap ? <Text style={styles.tokenBootstrap}>· bootstrap</Text> : null}
|
|
267
|
-
</Text>
|
|
268
|
-
<Text style={styles.tokenMeta}>
|
|
269
|
-
{status} · {last}
|
|
270
|
-
</Text>
|
|
271
|
-
</View>
|
|
272
|
-
{!isBootstrap && !token.revokedAt ? (
|
|
273
|
-
<Pressable onPress={onRevoke} hitSlop={10}>
|
|
274
|
-
<Text style={styles.tokenRevoke}>Revoke</Text>
|
|
275
|
-
</Pressable>
|
|
276
|
-
) : null}
|
|
277
|
-
</View>
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const THEME_OPTIONS: { value: ThemeMode; label: string }[] = [
|
|
282
|
-
{ value: 'system', label: 'System' },
|
|
283
|
-
{ value: 'light', label: 'Light' },
|
|
284
|
-
{ value: 'dark', label: 'Dark' },
|
|
285
|
-
]
|
|
286
|
-
|
|
287
|
-
function ThemePicker() {
|
|
288
|
-
const colors = useColors()
|
|
289
|
-
const styles = useMemo(() => makeStyles(colors), [colors])
|
|
290
|
-
const [mode, setMode] = useThemeMode()
|
|
291
|
-
return (
|
|
292
|
-
<View style={styles.segmentedRow}>
|
|
293
|
-
{THEME_OPTIONS.map((opt) => {
|
|
294
|
-
const active = mode === opt.value
|
|
295
|
-
return (
|
|
296
|
-
<Pressable
|
|
297
|
-
key={opt.value}
|
|
298
|
-
onPress={() => {
|
|
299
|
-
void setMode(opt.value)
|
|
300
|
-
}}
|
|
301
|
-
style={[styles.segment, active && styles.segmentActive]}
|
|
302
|
-
>
|
|
303
|
-
<Text style={[styles.segmentText, active && styles.segmentTextActive]}>{opt.label}</Text>
|
|
304
|
-
</Pressable>
|
|
305
|
-
)
|
|
306
|
-
})}
|
|
307
|
-
</View>
|
|
308
|
-
)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const makeStyles = (colors: Colors) =>
|
|
312
|
-
StyleSheet.create({
|
|
313
|
-
screen: { flex: 1, backgroundColor: colors.background },
|
|
314
|
-
content: { padding: 20, gap: 4 },
|
|
315
|
-
centered: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, gap: 12 },
|
|
316
|
-
section: { marginTop: 16 },
|
|
317
|
-
sectionLabel: {
|
|
318
|
-
fontSize: 11,
|
|
319
|
-
color: colors.mocha,
|
|
320
|
-
textTransform: 'uppercase',
|
|
321
|
-
letterSpacing: 0.5,
|
|
322
|
-
marginBottom: 6,
|
|
323
|
-
fontFamily: fonts.bodyMedium,
|
|
324
|
-
},
|
|
325
|
-
sectionBody: { gap: 4 },
|
|
326
|
-
mono: { fontFamily: fonts.mono, fontSize: 13, color: colors.charcoal },
|
|
327
|
-
divider: { height: StyleSheet.hairlineWidth, backgroundColor: colors.border, marginVertical: 16 },
|
|
328
|
-
mintRow: { flexDirection: 'row', gap: 8, alignItems: 'center' },
|
|
329
|
-
mintInput: {
|
|
330
|
-
flex: 1,
|
|
331
|
-
paddingHorizontal: 12,
|
|
332
|
-
paddingVertical: 10,
|
|
333
|
-
borderRadius: radii.md,
|
|
334
|
-
backgroundColor: colors.ivory,
|
|
335
|
-
fontSize: 14,
|
|
336
|
-
color: colors.foreground,
|
|
337
|
-
fontFamily: fonts.body,
|
|
338
|
-
},
|
|
339
|
-
mintBtn: {
|
|
340
|
-
paddingHorizontal: 16,
|
|
341
|
-
paddingVertical: 10,
|
|
342
|
-
borderRadius: radii.md,
|
|
343
|
-
backgroundColor: colors.sapphire,
|
|
344
|
-
},
|
|
345
|
-
mintBtnDisabled: { backgroundColor: colors.mochaLight },
|
|
346
|
-
mintBtnPressed: { backgroundColor: colors.sapphireDeep },
|
|
347
|
-
mintBtnText: { color: colors.primaryForeground, fontSize: 14, fontFamily: fonts.bodyMedium },
|
|
348
|
-
mintedBox: {
|
|
349
|
-
marginTop: 10,
|
|
350
|
-
padding: 12,
|
|
351
|
-
borderRadius: radii.md,
|
|
352
|
-
backgroundColor: colors.sapphireGlow,
|
|
353
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
354
|
-
borderColor: colors.sapphire,
|
|
355
|
-
gap: 6,
|
|
356
|
-
},
|
|
357
|
-
mintedLabel: {
|
|
358
|
-
fontSize: 11,
|
|
359
|
-
color: colors.accentForeground,
|
|
360
|
-
textTransform: 'uppercase',
|
|
361
|
-
letterSpacing: 0.5,
|
|
362
|
-
fontFamily: fonts.bodyMedium,
|
|
363
|
-
},
|
|
364
|
-
mintedToken: { fontFamily: fonts.mono, fontSize: 12, color: colors.charcoal },
|
|
365
|
-
mintedHint: { fontSize: 11, color: colors.accentForeground, fontFamily: fonts.body },
|
|
366
|
-
tokenRow: {
|
|
367
|
-
flexDirection: 'row',
|
|
368
|
-
alignItems: 'center',
|
|
369
|
-
paddingVertical: 10,
|
|
370
|
-
gap: 12,
|
|
371
|
-
},
|
|
372
|
-
tokenSep: { height: StyleSheet.hairlineWidth, backgroundColor: colors.frost },
|
|
373
|
-
tokenInfo: { flex: 1 },
|
|
374
|
-
tokenLabel: { fontSize: 14, color: colors.foreground, fontFamily: fonts.body },
|
|
375
|
-
tokenBootstrap: { color: colors.mochaLight, fontSize: 11, fontFamily: fonts.body },
|
|
376
|
-
tokenMeta: { fontSize: 11, color: colors.mochaLight, marginTop: 2, fontFamily: fonts.body },
|
|
377
|
-
tokenRevoke: { color: colors.destructive, fontSize: 13, fontFamily: fonts.bodyMedium },
|
|
378
|
-
primaryBtn: {
|
|
379
|
-
marginTop: 12,
|
|
380
|
-
paddingHorizontal: 20,
|
|
381
|
-
paddingVertical: 10,
|
|
382
|
-
borderRadius: radii.md,
|
|
383
|
-
backgroundColor: colors.sapphire,
|
|
384
|
-
},
|
|
385
|
-
primaryBtnText: { color: colors.primaryForeground, fontSize: 14, fontFamily: fonts.bodyMedium },
|
|
386
|
-
linkBtn: { marginTop: 4, padding: 8 },
|
|
387
|
-
linkBtnText: { color: colors.mocha, fontSize: 13, fontFamily: fonts.body },
|
|
388
|
-
dangerBtn: {
|
|
389
|
-
marginTop: 8,
|
|
390
|
-
paddingVertical: 12,
|
|
391
|
-
borderRadius: radii.md,
|
|
392
|
-
backgroundColor: colors.card,
|
|
393
|
-
borderWidth: 1,
|
|
394
|
-
borderColor: colors.destructive,
|
|
395
|
-
alignItems: 'center',
|
|
396
|
-
},
|
|
397
|
-
// ~10% destructive tint, tracks theme via the hex-alpha-suffix trick.
|
|
398
|
-
dangerBtnPressed: { backgroundColor: `${colors.destructive}1A` },
|
|
399
|
-
dangerBtnText: { color: colors.destructive, fontSize: 14, fontFamily: fonts.bodyMedium },
|
|
400
|
-
errorTitle: { fontSize: 18, fontFamily: fonts.bodyBold, color: colors.foreground },
|
|
401
|
-
errorBody: { color: colors.destructive, textAlign: 'center', fontFamily: fonts.body },
|
|
402
|
-
segmentedRow: {
|
|
403
|
-
flexDirection: 'row',
|
|
404
|
-
backgroundColor: colors.ivory,
|
|
405
|
-
borderRadius: radii.md,
|
|
406
|
-
padding: 2,
|
|
407
|
-
gap: 2,
|
|
408
|
-
},
|
|
409
|
-
segment: {
|
|
410
|
-
flex: 1,
|
|
411
|
-
paddingVertical: 8,
|
|
412
|
-
paddingHorizontal: 12,
|
|
413
|
-
borderRadius: radii.sm,
|
|
414
|
-
alignItems: 'center',
|
|
415
|
-
},
|
|
416
|
-
segmentActive: { backgroundColor: colors.card },
|
|
417
|
-
segmentText: { color: colors.mocha, fontSize: 13, fontFamily: fonts.body },
|
|
418
|
-
segmentTextActive: { color: colors.foreground, fontFamily: fonts.bodyMedium },
|
|
419
|
-
})
|
package/apps/mobile/app.json
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"expo": {
|
|
3
|
-
"name": "Bazilion",
|
|
4
|
-
"slug": "bazilion",
|
|
5
|
-
"version": "0.0.1",
|
|
6
|
-
"orientation": "portrait",
|
|
7
|
-
"scheme": "bazilion",
|
|
8
|
-
"userInterfaceStyle": "automatic",
|
|
9
|
-
"newArchEnabled": true,
|
|
10
|
-
"icon": "./assets/icon.png",
|
|
11
|
-
"splash": {
|
|
12
|
-
"image": "./assets/splash-icon.png",
|
|
13
|
-
"resizeMode": "contain",
|
|
14
|
-
"backgroundColor": "#F5F0E8"
|
|
15
|
-
},
|
|
16
|
-
"ios": {
|
|
17
|
-
"supportsTablet": true,
|
|
18
|
-
"bundleIdentifier": "dev.bazilion.mobile"
|
|
19
|
-
},
|
|
20
|
-
"android": {
|
|
21
|
-
"package": "dev.bazilion.mobile",
|
|
22
|
-
"adaptiveIcon": {
|
|
23
|
-
"foregroundImage": "./assets/adaptive-icon.png",
|
|
24
|
-
"backgroundColor": "#F5F0E8"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"web": {
|
|
28
|
-
"favicon": "./assets/favicon.png"
|
|
29
|
-
},
|
|
30
|
-
"plugins": [
|
|
31
|
-
"expo-router",
|
|
32
|
-
[
|
|
33
|
-
"expo-camera",
|
|
34
|
-
{
|
|
35
|
-
"cameraPermission": "Allow Bazilion to use the camera to scan pairing QR codes."
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
[
|
|
39
|
-
"expo-secure-store",
|
|
40
|
-
{
|
|
41
|
-
"faceIDPermission": "Allow Bazilion to authenticate with Face ID to unlock stored credentials."
|
|
42
|
-
}
|
|
43
|
-
]
|
|
44
|
-
],
|
|
45
|
-
"experiments": {
|
|
46
|
-
"typedRoutes": true
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// pnpm + Expo monorepo config.
|
|
2
|
-
//
|
|
3
|
-
// - `watchFolders` gets the workspace root APPENDED (not replaced) so Metro
|
|
4
|
-
// picks up live edits to `@bazilion/client` / `@bazilion/api-types` during
|
|
5
|
-
// dev while still keeping Expo's own watched paths.
|
|
6
|
-
//
|
|
7
|
-
// - `nodeModulesPaths` also includes the workspace root's `node_modules` so
|
|
8
|
-
// hoisted deps resolve. Hierarchical lookup stays enabled (the Expo
|
|
9
|
-
// default) — this lets Metro fall back to pnpm's `.pnpm/*` symlink store
|
|
10
|
-
// for transitive peers that aren't hoisted to `apps/mobile/node_modules`.
|
|
11
|
-
// Safe as long as there's exactly one copy of `react-native` / `react` in
|
|
12
|
-
// the tree; a fresh `pnpm install` after SDK bumps keeps it that way.
|
|
13
|
-
|
|
14
|
-
const { getDefaultConfig } = require('expo/metro-config')
|
|
15
|
-
const path = require('node:path')
|
|
16
|
-
|
|
17
|
-
const projectRoot = __dirname
|
|
18
|
-
const workspaceRoot = path.resolve(projectRoot, '../..')
|
|
19
|
-
|
|
20
|
-
const config = getDefaultConfig(projectRoot)
|
|
21
|
-
|
|
22
|
-
config.watchFolders = [...(config.watchFolders ?? []), workspaceRoot]
|
|
23
|
-
config.resolver.nodeModulesPaths = [
|
|
24
|
-
path.resolve(projectRoot, 'node_modules'),
|
|
25
|
-
path.resolve(workspaceRoot, 'node_modules'),
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
module.exports = config
|
package/apps/mobile/package.json
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bazilion/mobile",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"main": "expo-router/entry",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "expo start",
|
|
8
|
-
"ios": "expo start --ios",
|
|
9
|
-
"android": "expo start --android",
|
|
10
|
-
"typecheck": "tsc --noEmit"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@babel/runtime": "^7.25.0",
|
|
14
|
-
"@bazilion/api-types": "workspace:*",
|
|
15
|
-
"@bazilion/client": "workspace:*",
|
|
16
|
-
"@expo-google-fonts/dm-sans": "^0.4.2",
|
|
17
|
-
"@expo-google-fonts/dm-serif-display": "^0.4.2",
|
|
18
|
-
"@expo-google-fonts/jetbrains-mono": "^0.4.1",
|
|
19
|
-
"@expo/metro-runtime": "^6.1.2",
|
|
20
|
-
"@react-navigation/elements": "^2.9.14",
|
|
21
|
-
"expo": "^54.0.0",
|
|
22
|
-
"expo-camera": "~17.0.10",
|
|
23
|
-
"expo-constants": "~18.0.13",
|
|
24
|
-
"expo-font": "^55.0.6",
|
|
25
|
-
"expo-linking": "~8.0.11",
|
|
26
|
-
"expo-router": "~6.0.23",
|
|
27
|
-
"expo-secure-store": "~15.0.8",
|
|
28
|
-
"expo-status-bar": "~3.0.9",
|
|
29
|
-
"react": "19.1.0",
|
|
30
|
-
"react-dom": "19.1.0",
|
|
31
|
-
"react-native": "0.81.5",
|
|
32
|
-
"react-native-gesture-handler": "~2.28.0",
|
|
33
|
-
"react-native-markdown-display": "^7.0.2",
|
|
34
|
-
"react-native-reanimated": "~4.1.1",
|
|
35
|
-
"react-native-safe-area-context": "~5.6.0",
|
|
36
|
-
"react-native-screens": "~4.16.0",
|
|
37
|
-
"react-native-web": "^0.21.0",
|
|
38
|
-
"react-native-worklets": "0.5.1"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/react": "~19.1.10",
|
|
42
|
-
"typescript": "^5.7.0"
|
|
43
|
-
}
|
|
44
|
-
}
|
package/apps/mobile/src/auth.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { type BazilionClient, createClient } from '@bazilion/client'
|
|
2
|
-
import * as SecureStore from 'expo-secure-store'
|
|
3
|
-
|
|
4
|
-
const KEY_SERVER = 'bazilion.server'
|
|
5
|
-
const KEY_TOKEN = 'bazilion.token'
|
|
6
|
-
|
|
7
|
-
export interface Credentials {
|
|
8
|
-
server: string
|
|
9
|
-
token: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function loadCredentials(): Promise<Credentials | null> {
|
|
13
|
-
const [server, token] = await Promise.all([
|
|
14
|
-
SecureStore.getItemAsync(KEY_SERVER),
|
|
15
|
-
SecureStore.getItemAsync(KEY_TOKEN),
|
|
16
|
-
])
|
|
17
|
-
if (!server || !token) return null
|
|
18
|
-
return { server, token }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function saveCredentials(creds: Credentials): Promise<void> {
|
|
22
|
-
await Promise.all([
|
|
23
|
-
SecureStore.setItemAsync(KEY_SERVER, creds.server),
|
|
24
|
-
SecureStore.setItemAsync(KEY_TOKEN, creds.token),
|
|
25
|
-
])
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function clearCredentials(): Promise<void> {
|
|
29
|
-
await Promise.all([
|
|
30
|
-
SecureStore.deleteItemAsync(KEY_SERVER),
|
|
31
|
-
SecureStore.deleteItemAsync(KEY_TOKEN),
|
|
32
|
-
])
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Probe the daemon so we surface a clear error on the pairing screen instead
|
|
37
|
-
* of a confusing network failure on the first protected request. 5-second
|
|
38
|
-
* timeout because the daemon is a local-network service — if it can't answer
|
|
39
|
-
* in that long, something's wrong.
|
|
40
|
-
*/
|
|
41
|
-
export async function verifyCredentials(creds: Credentials): Promise<void> {
|
|
42
|
-
const ctrl = new AbortController()
|
|
43
|
-
const timer = setTimeout(() => ctrl.abort(), 5000)
|
|
44
|
-
try {
|
|
45
|
-
const res = await fetch(`${creds.server}/api/health`, {
|
|
46
|
-
headers: {
|
|
47
|
-
authorization: `Bearer ${creds.token}`,
|
|
48
|
-
origin: creds.server,
|
|
49
|
-
},
|
|
50
|
-
signal: ctrl.signal,
|
|
51
|
-
})
|
|
52
|
-
if (res.status === 401) throw new Error('server rejected the token')
|
|
53
|
-
if (!res.ok) throw new Error(`server returned ${res.status} from /api/health`)
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (err instanceof Error && err.name === 'AbortError') {
|
|
56
|
-
throw new Error(`could not reach ${creds.server} within 5s`)
|
|
57
|
-
}
|
|
58
|
-
throw err
|
|
59
|
-
} finally {
|
|
60
|
-
clearTimeout(timer)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function clientFor(creds: Credentials): BazilionClient {
|
|
65
|
-
return createClient({ serverUrl: creds.server, token: creds.token })
|
|
66
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
export interface ParsedPairing {
|
|
2
|
-
server: string
|
|
3
|
-
token: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export class PairUrlError extends Error {
|
|
7
|
-
constructor(message: string) {
|
|
8
|
-
super(message)
|
|
9
|
-
this.name = 'PairUrlError'
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Parse a `bazilion://pair?server=<url>&token=<t>` URL emitted by
|
|
15
|
-
* `bazilion token create --qr`. The scheme and host are both mandatory so a
|
|
16
|
-
* stray http(s) URL or a typo surfaces as a clear error instead of a
|
|
17
|
-
* confusing downstream network failure.
|
|
18
|
-
*/
|
|
19
|
-
export function parsePairingUrl(raw: string): ParsedPairing {
|
|
20
|
-
let url: URL
|
|
21
|
-
try {
|
|
22
|
-
url = new URL(raw)
|
|
23
|
-
} catch {
|
|
24
|
-
throw new PairUrlError(`not a valid URL: ${raw}`)
|
|
25
|
-
}
|
|
26
|
-
if (url.protocol !== 'bazilion:') {
|
|
27
|
-
throw new PairUrlError(`expected bazilion:// scheme, got ${url.protocol}`)
|
|
28
|
-
}
|
|
29
|
-
// Custom-scheme URLs parse `pair` as the hostname, not pathname.
|
|
30
|
-
if (url.hostname !== 'pair') {
|
|
31
|
-
throw new PairUrlError(`expected bazilion://pair, got bazilion://${url.hostname}`)
|
|
32
|
-
}
|
|
33
|
-
const server = url.searchParams.get('server')
|
|
34
|
-
const token = url.searchParams.get('token')
|
|
35
|
-
if (!server) throw new PairUrlError('missing ?server=')
|
|
36
|
-
if (!token) throw new PairUrlError('missing ?token=')
|
|
37
|
-
try {
|
|
38
|
-
const serverUrl = new URL(server)
|
|
39
|
-
if (serverUrl.protocol !== 'http:' && serverUrl.protocol !== 'https:') {
|
|
40
|
-
throw new PairUrlError(`server must be http(s), got ${serverUrl.protocol}`)
|
|
41
|
-
}
|
|
42
|
-
} catch (err) {
|
|
43
|
-
if (err instanceof PairUrlError) throw err
|
|
44
|
-
throw new PairUrlError(`server is not a valid URL: ${server}`)
|
|
45
|
-
}
|
|
46
|
-
// Strip trailing slash so `${server}${path}` never produces `//`.
|
|
47
|
-
return { server: server.replace(/\/$/, ''), token }
|
|
48
|
-
}
|