pinokiod 3.250.0 → 3.251.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/kernel/peer.js +175 -22
- package/package.json +1 -1
- package/server/index.js +113 -50
- package/server/public/style.css +13 -0
- package/server/public/tab-link-popover.js +264 -28
- package/server/views/agents.ejs +2 -10
- package/server/views/app.ejs +3 -3
- package/server/views/connect.ejs +1 -9
- package/server/views/index.ejs +132 -32
- package/server/views/init/index.ejs +1 -9
- package/server/views/net.ejs +72 -11
- package/server/views/network.ejs +2 -10
- package/server/views/partials/peer_access_points.ejs +27 -0
- package/server/views/review.ejs +3 -3
- package/server/views/screenshots.ejs +1 -9
- package/server/views/settings.ejs +1 -9
- package/server/views/shell.ejs +3 -0
- package/server/views/terminal.ejs +3 -0
- package/server/views/tools.ejs +1 -9
|
@@ -149,6 +149,81 @@
|
|
|
149
149
|
|
|
150
150
|
const isIPv4Host = (host) => /^(\d{1,3}\.){3}\d{1,3}$/.test((host || '').trim())
|
|
151
151
|
|
|
152
|
+
const normalizeHostValue = (value) => {
|
|
153
|
+
if (!value || typeof value !== 'string') {
|
|
154
|
+
return ''
|
|
155
|
+
}
|
|
156
|
+
return value.trim().toLowerCase()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const classifyHostScope = (host) => {
|
|
160
|
+
const value = normalizeHostValue(host)
|
|
161
|
+
if (!value) {
|
|
162
|
+
return 'unknown'
|
|
163
|
+
}
|
|
164
|
+
if (value === 'localhost' || value === '0.0.0.0' || value.startsWith('127.')) {
|
|
165
|
+
return 'loopback'
|
|
166
|
+
}
|
|
167
|
+
if (/^10\./.test(value)) {
|
|
168
|
+
return 'lan'
|
|
169
|
+
}
|
|
170
|
+
if (/^192\.168\./.test(value)) {
|
|
171
|
+
return 'lan'
|
|
172
|
+
}
|
|
173
|
+
if (/^172\.(1[6-9]|2[0-9]|3[01])\./.test(value)) {
|
|
174
|
+
return 'lan'
|
|
175
|
+
}
|
|
176
|
+
if (/^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(value)) {
|
|
177
|
+
return 'cgnat'
|
|
178
|
+
}
|
|
179
|
+
if (/^169\.254\./.test(value) || value.startsWith('fe80:')) {
|
|
180
|
+
return 'linklocal'
|
|
181
|
+
}
|
|
182
|
+
return 'public'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const scopeToBadge = (scope) => {
|
|
186
|
+
if (!scope || typeof scope !== 'string') {
|
|
187
|
+
return ''
|
|
188
|
+
}
|
|
189
|
+
const normalized = scope.trim().toLowerCase()
|
|
190
|
+
switch (normalized) {
|
|
191
|
+
case 'lan':
|
|
192
|
+
return 'LAN'
|
|
193
|
+
case 'cgnat':
|
|
194
|
+
return 'VPN'
|
|
195
|
+
case 'public':
|
|
196
|
+
return 'Public'
|
|
197
|
+
case 'loopback':
|
|
198
|
+
return 'Local'
|
|
199
|
+
case 'linklocal':
|
|
200
|
+
return 'Link-Local'
|
|
201
|
+
default:
|
|
202
|
+
return ''
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const mergeMeta = (existing, incoming) => {
|
|
207
|
+
if (!incoming) {
|
|
208
|
+
return existing || null
|
|
209
|
+
}
|
|
210
|
+
if (!existing) {
|
|
211
|
+
return { ...incoming }
|
|
212
|
+
}
|
|
213
|
+
const merged = { ...existing }
|
|
214
|
+
const assignIfMissing = (key) => {
|
|
215
|
+
if ((merged[key] === undefined || merged[key] === null || merged[key] === '') && incoming[key]) {
|
|
216
|
+
merged[key] = incoming[key]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
assignIfMissing('scope')
|
|
220
|
+
assignIfMissing('interface')
|
|
221
|
+
assignIfMissing('source')
|
|
222
|
+
assignIfMissing('host')
|
|
223
|
+
assignIfMissing('port')
|
|
224
|
+
return merged
|
|
225
|
+
}
|
|
226
|
+
|
|
152
227
|
const extractProjectSlug = (node) => {
|
|
153
228
|
if (!node) {
|
|
154
229
|
return ""
|
|
@@ -424,6 +499,7 @@
|
|
|
424
499
|
const hostPortMap = new Map()
|
|
425
500
|
const externalHttpByExtPort = new Map() // ext port -> Set of host:port (external_ip)
|
|
426
501
|
const externalHttpByIntPort = new Map() // internal port -> Set of host:port (external_ip)
|
|
502
|
+
const externalHostMeta = new Map() // host:port -> meta info
|
|
427
503
|
const hostAliasPortMap = new Map()
|
|
428
504
|
if (data?.router && typeof data.router === "object") {
|
|
429
505
|
Object.entries(data.router).forEach(([dial, hosts]) => {
|
|
@@ -451,6 +527,50 @@
|
|
|
451
527
|
}
|
|
452
528
|
const localAliases = ["127.0.0.1", "localhost", "0.0.0.0", "::1", "[::1]"]
|
|
453
529
|
|
|
530
|
+
const registerExternalHttpHost = ({ host, port, internalPort, scope, iface, source }) => {
|
|
531
|
+
if (!host || !port) {
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
const hostTrimmed = `${host}`.trim()
|
|
535
|
+
const portTrimmed = `${port}`.trim()
|
|
536
|
+
if (!hostTrimmed || !portTrimmed) {
|
|
537
|
+
return
|
|
538
|
+
}
|
|
539
|
+
const hostPort = `${hostTrimmed}:${portTrimmed}`
|
|
540
|
+
if (!externalHttpByExtPort.has(portTrimmed)) {
|
|
541
|
+
externalHttpByExtPort.set(portTrimmed, new Set())
|
|
542
|
+
}
|
|
543
|
+
externalHttpByExtPort.get(portTrimmed).add(hostPort)
|
|
544
|
+
if (internalPort) {
|
|
545
|
+
const intKey = `${internalPort}`.trim()
|
|
546
|
+
if (intKey) {
|
|
547
|
+
if (!externalHttpByIntPort.has(intKey)) {
|
|
548
|
+
externalHttpByIntPort.set(intKey, new Set())
|
|
549
|
+
}
|
|
550
|
+
externalHttpByIntPort.get(intKey).add(hostPort)
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (!externalHostMeta.has(hostPort)) {
|
|
554
|
+
const inferredScope = scope || classifyHostScope(hostTrimmed)
|
|
555
|
+
externalHostMeta.set(hostPort, {
|
|
556
|
+
scope: inferredScope,
|
|
557
|
+
interface: iface || null,
|
|
558
|
+
source: source || null,
|
|
559
|
+
host: hostTrimmed,
|
|
560
|
+
port: portTrimmed
|
|
561
|
+
})
|
|
562
|
+
} else {
|
|
563
|
+
const current = externalHostMeta.get(hostPort)
|
|
564
|
+
externalHostMeta.set(hostPort, mergeMeta(current, {
|
|
565
|
+
scope: scope || classifyHostScope(hostTrimmed),
|
|
566
|
+
interface: iface || null,
|
|
567
|
+
source: source || null,
|
|
568
|
+
host: hostTrimmed,
|
|
569
|
+
port: portTrimmed
|
|
570
|
+
}))
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
454
574
|
const addHttpMapping = (host, port, httpsSet) => {
|
|
455
575
|
if (!host || !port || !httpsSet || httpsSet.size === 0) {
|
|
456
576
|
return
|
|
@@ -576,20 +696,59 @@
|
|
|
576
696
|
// Record external http host:port candidates by external and internal ports for later
|
|
577
697
|
if (entry.external_ip && typeof entry.external_ip === 'string') {
|
|
578
698
|
const parsed = parseHostPort(entry.external_ip)
|
|
579
|
-
if (parsed && parsed.port) {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
699
|
+
if (parsed && parsed.host && parsed.port) {
|
|
700
|
+
registerExternalHttpHost({
|
|
701
|
+
host: parsed.host,
|
|
702
|
+
port: parsed.port,
|
|
703
|
+
internalPort: entry.internal_port,
|
|
704
|
+
source: 'external_ip'
|
|
705
|
+
})
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (Array.isArray(entry.external_hosts)) {
|
|
709
|
+
entry.external_hosts.forEach((hostEntry) => {
|
|
710
|
+
if (!hostEntry) {
|
|
711
|
+
return
|
|
583
712
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
713
|
+
if (typeof hostEntry === 'string') {
|
|
714
|
+
const parsed = parseHostPort(hostEntry)
|
|
715
|
+
if (parsed && parsed.host && parsed.port) {
|
|
716
|
+
registerExternalHttpHost({
|
|
717
|
+
host: parsed.host,
|
|
718
|
+
port: parsed.port,
|
|
719
|
+
internalPort: entry.internal_port,
|
|
720
|
+
source: 'external_hosts'
|
|
721
|
+
})
|
|
589
722
|
}
|
|
590
|
-
|
|
723
|
+
return
|
|
591
724
|
}
|
|
592
|
-
|
|
725
|
+
if (typeof hostEntry === 'object') {
|
|
726
|
+
let host = typeof hostEntry.host === 'string' ? hostEntry.host : null
|
|
727
|
+
if (!host && typeof hostEntry.address === 'string') {
|
|
728
|
+
host = hostEntry.address
|
|
729
|
+
}
|
|
730
|
+
let portValue = hostEntry.port || hostEntry.external_port
|
|
731
|
+
if ((!host || !portValue) && typeof hostEntry.url === 'string') {
|
|
732
|
+
const parsed = parseHostPort(hostEntry.url)
|
|
733
|
+
if (parsed) {
|
|
734
|
+
if (!host && parsed.host) {
|
|
735
|
+
host = parsed.host
|
|
736
|
+
}
|
|
737
|
+
if (!portValue && parsed.port) {
|
|
738
|
+
portValue = parsed.port
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
registerExternalHttpHost({
|
|
743
|
+
host,
|
|
744
|
+
port: portValue,
|
|
745
|
+
internalPort: entry.internal_port,
|
|
746
|
+
scope: typeof hostEntry.scope === 'string' ? hostEntry.scope : null,
|
|
747
|
+
iface: typeof hostEntry.interface === 'string' ? hostEntry.interface : null,
|
|
748
|
+
source: 'external_hosts'
|
|
749
|
+
})
|
|
750
|
+
}
|
|
751
|
+
})
|
|
593
752
|
}
|
|
594
753
|
|
|
595
754
|
if (httpsTargets.size === 0) {
|
|
@@ -668,7 +827,8 @@
|
|
|
668
827
|
portMap,
|
|
669
828
|
hostPortMap,
|
|
670
829
|
externalHttpByExtPort,
|
|
671
|
-
externalHttpByIntPort
|
|
830
|
+
externalHttpByIntPort,
|
|
831
|
+
externalHostMeta
|
|
672
832
|
}
|
|
673
833
|
})
|
|
674
834
|
.catch(() => {
|
|
@@ -677,7 +837,8 @@
|
|
|
677
837
|
portMap: new Map(),
|
|
678
838
|
hostPortMap: new Map(),
|
|
679
839
|
externalHttpByExtPort: new Map(),
|
|
680
|
-
externalHttpByIntPort: new Map()
|
|
840
|
+
externalHttpByIntPort: new Map(),
|
|
841
|
+
externalHostMeta: new Map()
|
|
681
842
|
}
|
|
682
843
|
})
|
|
683
844
|
tabLinkRouterInfoExpiry = now + 3000
|
|
@@ -781,14 +942,21 @@
|
|
|
781
942
|
if (entryByUrl.has(canonical)) {
|
|
782
943
|
const existing = entryByUrl.get(canonical)
|
|
783
944
|
if (opts && opts.qr === true) existing.qr = true
|
|
945
|
+
if (opts && opts.meta) {
|
|
946
|
+
existing.meta = mergeMeta(existing.meta, opts.meta)
|
|
947
|
+
existing.badge = scopeToBadge(existing.meta && existing.meta.scope ? existing.meta.scope : '')
|
|
948
|
+
}
|
|
784
949
|
return
|
|
785
950
|
}
|
|
951
|
+
const entryMeta = opts && opts.meta ? opts.meta : null
|
|
786
952
|
const entry = {
|
|
787
953
|
type,
|
|
788
954
|
label,
|
|
789
955
|
url: canonical,
|
|
790
956
|
display: formatDisplayUrl(canonical),
|
|
791
|
-
qr: opts && opts.qr === true
|
|
957
|
+
qr: opts && opts.qr === true,
|
|
958
|
+
meta: entryMeta,
|
|
959
|
+
badge: scopeToBadge(entryMeta && entryMeta.scope ? entryMeta.scope : '')
|
|
792
960
|
}
|
|
793
961
|
entryByUrl.set(canonical, entry)
|
|
794
962
|
entries.push(entry)
|
|
@@ -802,11 +970,25 @@
|
|
|
802
970
|
addEntry("url", "URL", baseHref, { allowSameOrigin: true })
|
|
803
971
|
}
|
|
804
972
|
|
|
805
|
-
const httpCandidates = new Map() // url -> { qr: boolean }
|
|
973
|
+
const httpCandidates = new Map() // url -> { qr: boolean, meta: object|null }
|
|
806
974
|
const httpsCandidates = new Set()
|
|
807
975
|
|
|
976
|
+
const upsertHttpCandidate = (url, { qr = false, meta = null } = {}) => {
|
|
977
|
+
if (!url) {
|
|
978
|
+
return
|
|
979
|
+
}
|
|
980
|
+
const existing = httpCandidates.get(url) || { qr: false, meta: null }
|
|
981
|
+
if (qr === true) {
|
|
982
|
+
existing.qr = true
|
|
983
|
+
}
|
|
984
|
+
if (meta) {
|
|
985
|
+
existing.meta = mergeMeta(existing.meta, meta)
|
|
986
|
+
}
|
|
987
|
+
httpCandidates.set(url, existing)
|
|
988
|
+
}
|
|
989
|
+
|
|
808
990
|
if (isHttpUrl(baseHref)) {
|
|
809
|
-
|
|
991
|
+
upsertHttpCandidate(canonicalBase || canonicalizeUrl(baseHref), { qr: false })
|
|
810
992
|
} else if (isHttpsUrl(baseHref)) {
|
|
811
993
|
if (canonicalBase) {
|
|
812
994
|
httpsCandidates.add(canonicalBase)
|
|
@@ -828,10 +1010,10 @@
|
|
|
828
1010
|
const normalizedPath = pathname.toLowerCase()
|
|
829
1011
|
if (normalizedPath.includes(`/asset/api/${projectSlug}`)) {
|
|
830
1012
|
const fallbackHttp = `http://127.0.0.1:42000${pathname}`
|
|
831
|
-
|
|
1013
|
+
upsertHttpCandidate(canonicalizeUrl(fallbackHttp), { qr: false })
|
|
832
1014
|
} else if (normalizedPath.includes(`/api/${projectSlug}`)) {
|
|
833
1015
|
const fallbackHttp = `http://127.0.0.1:42000/asset/api/${projectSlug}/`
|
|
834
|
-
|
|
1016
|
+
upsertHttpCandidate(canonicalizeUrl(fallbackHttp), { qr: false })
|
|
835
1017
|
}
|
|
836
1018
|
} catch (_) {
|
|
837
1019
|
// ignore fallback errors
|
|
@@ -855,8 +1037,7 @@
|
|
|
855
1037
|
if (isHttpsUrl(canonical)) {
|
|
856
1038
|
httpsCandidates.add(canonical)
|
|
857
1039
|
} else if (isHttpUrl(canonical)) {
|
|
858
|
-
|
|
859
|
-
httpCandidates.set(canonical, { qr: prev ? prev.qr === true : false })
|
|
1040
|
+
upsertHttpCandidate(canonical, { qr: false })
|
|
860
1041
|
}
|
|
861
1042
|
})
|
|
862
1043
|
})
|
|
@@ -886,8 +1067,8 @@
|
|
|
886
1067
|
const hpUrl = `http://${hostport}${base.pathname || '/'}${base.search || ''}`
|
|
887
1068
|
const canonical = canonicalizeUrl(hpUrl)
|
|
888
1069
|
if (isHttpUrl(canonical)) {
|
|
889
|
-
const
|
|
890
|
-
|
|
1070
|
+
const meta = routerData && routerData.externalHostMeta ? routerData.externalHostMeta.get(hostport) : null
|
|
1071
|
+
upsertHttpCandidate(canonical, { qr: true, meta })
|
|
891
1072
|
}
|
|
892
1073
|
} catch (_) {}
|
|
893
1074
|
})
|
|
@@ -909,8 +1090,7 @@
|
|
|
909
1090
|
const hostPort = parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname
|
|
910
1091
|
const httpUrl = `http://${hostPort}${parsed.pathname || "/"}${parsed.search || ""}`
|
|
911
1092
|
const key = canonicalizeUrl(httpUrl)
|
|
912
|
-
|
|
913
|
-
httpCandidates.set(key, { qr: prev ? prev.qr === true : false })
|
|
1093
|
+
upsertHttpCandidate(key, { qr: false })
|
|
914
1094
|
} catch (_) {
|
|
915
1095
|
// ignore failures
|
|
916
1096
|
}
|
|
@@ -920,8 +1100,8 @@
|
|
|
920
1100
|
const httpList = Array.from(httpCandidates.keys()).sort()
|
|
921
1101
|
|
|
922
1102
|
httpList.forEach((url) => {
|
|
923
|
-
const
|
|
924
|
-
addEntry("http", "HTTP", url, { qr:
|
|
1103
|
+
const candidate = httpCandidates.get(url) || { qr: false, meta: null }
|
|
1104
|
+
addEntry("http", "HTTP", url, { qr: candidate.qr === true, meta: candidate.meta || null })
|
|
925
1105
|
})
|
|
926
1106
|
httpsList.forEach((url) => {
|
|
927
1107
|
addEntry("https", "HTTPS", url)
|
|
@@ -954,6 +1134,28 @@
|
|
|
954
1134
|
if (peerHostLower !== "localhost" && !peerHostLower.startsWith("127.")) {
|
|
955
1135
|
const baseUrl = parsedBaseUrl || new URL(baseHref, location.origin)
|
|
956
1136
|
const baseHostLower = (baseUrl.hostname || "").toLowerCase()
|
|
1137
|
+
const candidateList = Array.isArray(peerInfo?.host_candidates) ? peerInfo.host_candidates : []
|
|
1138
|
+
const metaForHost = (hostValue, source = 'peer-fallback') => {
|
|
1139
|
+
if (!hostValue) {
|
|
1140
|
+
return null
|
|
1141
|
+
}
|
|
1142
|
+
const normalized = normalizeHostValue(hostValue)
|
|
1143
|
+
const match = candidateList.find((candidate) => normalizeHostValue(candidate && candidate.address) === normalized)
|
|
1144
|
+
if (match) {
|
|
1145
|
+
return {
|
|
1146
|
+
scope: typeof match.scope === 'string' ? match.scope : classifyHostScope(hostValue),
|
|
1147
|
+
interface: typeof match.interface === 'string' ? match.interface : null,
|
|
1148
|
+
source: 'peer-candidate',
|
|
1149
|
+
host: match.address || hostValue
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return {
|
|
1153
|
+
scope: classifyHostScope(hostValue),
|
|
1154
|
+
interface: null,
|
|
1155
|
+
source,
|
|
1156
|
+
host: hostValue
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
957
1159
|
if (peerHostLower !== baseHostLower) {
|
|
958
1160
|
const baseProtocol = baseUrl.protocol ? baseUrl.protocol.toLowerCase() : "http:"
|
|
959
1161
|
const scheme = baseProtocol === "https:" ? "https://" : "http://"
|
|
@@ -963,7 +1165,40 @@
|
|
|
963
1165
|
const searchSegment = baseUrl.search || ""
|
|
964
1166
|
const fallbackUrl = `${scheme}${hostPort}${pathSegment}${searchSegment}`
|
|
965
1167
|
const label = baseProtocol === "https:" ? "HTTPS" : "HTTP"
|
|
966
|
-
addEntry(baseProtocol === "https:" ? "https" : "http", label, fallbackUrl, { qr: true })
|
|
1168
|
+
addEntry(baseProtocol === "https:" ? "https" : "http", label, fallbackUrl, { qr: true, meta: metaForHost(peerHost) })
|
|
1169
|
+
}
|
|
1170
|
+
if (candidateList.length > 0) {
|
|
1171
|
+
candidateList.forEach((candidate) => {
|
|
1172
|
+
if (!candidate || typeof candidate.address !== "string") {
|
|
1173
|
+
return
|
|
1174
|
+
}
|
|
1175
|
+
const candidateHost = candidate.address.trim()
|
|
1176
|
+
if (!candidateHost) {
|
|
1177
|
+
return
|
|
1178
|
+
}
|
|
1179
|
+
const candidateHostLower = candidateHost.toLowerCase()
|
|
1180
|
+
if (candidateHostLower === peerHostLower) {
|
|
1181
|
+
return
|
|
1182
|
+
}
|
|
1183
|
+
if (candidateHostLower === "localhost" || candidateHostLower.startsWith("127.")) {
|
|
1184
|
+
return
|
|
1185
|
+
}
|
|
1186
|
+
const baseProtocol = baseUrl.protocol ? baseUrl.protocol.toLowerCase() : "http:"
|
|
1187
|
+
const scheme = baseProtocol === "https:" ? "https://" : "http://"
|
|
1188
|
+
const port = baseUrl.port || (baseProtocol === "https:" ? "443" : "80")
|
|
1189
|
+
const hostPort = port ? `${candidateHost}:${port}` : candidateHost
|
|
1190
|
+
const pathSegment = baseUrl.pathname || "/"
|
|
1191
|
+
const searchSegment = baseUrl.search || ""
|
|
1192
|
+
const fallbackUrl = `${scheme}${hostPort}${pathSegment}${searchSegment}`
|
|
1193
|
+
const label = baseProtocol === "https:" ? "HTTPS" : "HTTP"
|
|
1194
|
+
const entryMeta = {
|
|
1195
|
+
scope: typeof candidate.scope === 'string' ? candidate.scope : classifyHostScope(candidateHost),
|
|
1196
|
+
interface: typeof candidate.interface === 'string' ? candidate.interface : null,
|
|
1197
|
+
source: 'peer-candidate',
|
|
1198
|
+
host: candidateHost
|
|
1199
|
+
}
|
|
1200
|
+
addEntry(baseProtocol === "https:" ? "https" : "http", label, fallbackUrl, { qr: true, meta: entryMeta })
|
|
1201
|
+
})
|
|
967
1202
|
}
|
|
968
1203
|
}
|
|
969
1204
|
}
|
|
@@ -1281,7 +1516,8 @@
|
|
|
1281
1516
|
item.setAttribute("data-url", entry.url)
|
|
1282
1517
|
const labelSpan = document.createElement("span")
|
|
1283
1518
|
labelSpan.className = "label"
|
|
1284
|
-
|
|
1519
|
+
const labelText = entry && entry.badge ? `${entry.label} (${entry.badge})` : entry.label
|
|
1520
|
+
labelSpan.textContent = labelText
|
|
1285
1521
|
const valueSpan = document.createElement("span")
|
|
1286
1522
|
valueSpan.className = "value"
|
|
1287
1523
|
valueSpan.textContent = entry.display
|
package/server/views/agents.ejs
CHANGED
|
@@ -518,16 +518,8 @@ body.dark .plugin-option:hover {
|
|
|
518
518
|
<a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
|
|
519
519
|
<a class='tab selected' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
|
|
520
520
|
<a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
<a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
|
|
524
|
-
<img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
|
|
525
|
-
<div class='caption'>Scan to open</div>
|
|
526
|
-
<div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
|
|
527
|
-
</a>
|
|
528
|
-
</div>
|
|
529
|
-
<% } %>
|
|
530
|
-
</aside>
|
|
521
|
+
<%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
|
|
522
|
+
</aside>
|
|
531
523
|
</main>
|
|
532
524
|
<script type="application/json" id="plugin-data"><%- JSON.stringify(serializedPlugins) %></script>
|
|
533
525
|
<script type="application/json" id="plugin-app-data"><%- JSON.stringify(apps) %></script>
|
package/server/views/app.ejs
CHANGED
|
@@ -2982,10 +2982,10 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
2982
2982
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
2983
2983
|
<div class='sep'></div>
|
|
2984
2984
|
<div class='mode-selector'>
|
|
2985
|
-
<a class="btn2 <%=type === 'review' ? 'selected' : ''%>" href="<%=review_tab%>"><div><i class="fa-regular fa-message"></i></div><div class='caption'>Forum</div></a>
|
|
2986
|
-
<a class="btn2 <%=type === 'files' ? 'selected' : ''%>" href="<%=files_tab%>"><div><i class="fa-solid fa-file-lines"></i></div><div class='caption'>Files</div></a>
|
|
2987
|
-
<a class="btn2 <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>"><div><i class="fa-solid fa-code"></i></div><div class='caption'>Dev</div></a>
|
|
2988
2985
|
<a class="btn2 <%=type === 'run' ? 'selected' : ''%>" href="<%=run_tab%>"><div><i class="fa-solid fa-circle-play"></i></div><div class='caption'>Run</div></a>
|
|
2986
|
+
<a class="btn2 <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>"><div><i class="fa-solid fa-code"></i></div><div class='caption'>Dev</div></a>
|
|
2987
|
+
<a class="btn2 <%=type === 'files' ? 'selected' : ''%>" href="<%=files_tab%>"><div><i class="fa-solid fa-file-lines"></i></div><div class='caption'>Files</div></a>
|
|
2988
|
+
<a class="btn2 <%=type === 'review' ? 'selected' : ''%>" href="<%=review_tab%>"><div><i class="fa-regular fa-message"></i></div><div class='caption'>Forum</div></a>
|
|
2989
2989
|
</div>
|
|
2990
2990
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
2991
2991
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
package/server/views/connect.ejs
CHANGED
|
@@ -925,15 +925,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
925
925
|
<a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
|
|
926
926
|
<a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
|
|
927
927
|
<a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
|
|
928
|
-
|
|
929
|
-
<div class='qr' style='padding:12px 10px; text-align:center;'>
|
|
930
|
-
<a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
|
|
931
|
-
<img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
|
|
932
|
-
<div class='caption'>Scan to open</div>
|
|
933
|
-
<div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
|
|
934
|
-
</a>
|
|
935
|
-
</div>
|
|
936
|
-
<% } %>
|
|
928
|
+
<%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
|
|
937
929
|
</aside>
|
|
938
930
|
</main>
|
|
939
931
|
</body>
|