feat: support simultaneous Tailscale and LAN communication for Yahboom cameras

This commit is contained in:
2026-09-30 15:37:17 +09:00
parent 9387ade537
commit 08f74575c2
38 changed files with 917 additions and 142 deletions
+48
View File
@@ -0,0 +1,48 @@
import { readdir, readFile, realpath } from 'fs/promises'
import { resolve, sep } from 'path'
import type { LocalGitModule } from '../shared/gitModuleImport'
export async function localGitModulePath(root: string, folder: string): Promise<string> {
if (!/^\[lms-nav2-custom\][a-z][a-z0-9_]*_v[^/\\]+_ws$/.test(folder)) throw new Error('Invalid custom module folder')
const base = await realpath(root)
const target = await realpath(resolve(base, folder))
if (!target.startsWith(base + sep)) throw new Error('Module is outside the local workspace')
return target
}
export async function listLocalGitModules(root: string): Promise<LocalGitModule[]> {
const entries = await readdir(root, { withFileTypes: true }).catch((error: NodeJS.ErrnoException) => {
if (error.code === 'ENOENT') return []
throw error
})
const modules: LocalGitModule[] = []
for (const entry of entries) {
if (!entry.isDirectory() || !entry.name.startsWith('[lms-nav2-custom]')) continue
try {
const target = await localGitModulePath(root, entry.name)
const data = JSON.parse(await readFile(resolve(target, '.autonomy-studio-module.json'), 'utf8'))
if (data.sourceType !== 'CUSTOM' || !['name', 'category', 'packageName', 'launchFile'].every(key => typeof data[key] === 'string' && data[key])) continue
modules.push({ name: data.name, category: data.category, packageName: data.packageName, launchFile: data.launchFile, folderName: entry.name })
} catch { /* Ignore incomplete module folders. */ }
}
return modules.sort((a, b) => a.name.localeCompare(b.name))
}
export function repositoryAddress(value: string): string {
// Do not allow local paths, Git remote helpers, or credentials in the UI.
const normalized = /^git@[^:]+:.+/.test(value) ? value.replace(/^git@([^:]+):(.+)$/, 'ssh://git@$1/$2') : value
const url = new URL(normalized)
if (!['https:', 'http:', 'ssh:'].includes(url.protocol)) throw new Error('Unsupported Git repository protocol')
url.password = ''
if (url.protocol !== 'ssh:') url.username = ''
url.search = ''
url.hash = ''
return url.toString()
}
export function remoteBranches(output: string): Array<{ name: string; commit: string }> {
return output.split('\n').flatMap(line => {
const match = line.trim().match(/^([a-f0-9]{40,64})\s+refs\/heads\/(.+)$/)
return match ? [{ name: match[2], commit: match[1] }] : []
}).sort((a, b) => a.name.localeCompare(b.name))
}