dsh-tabbit 0.2.3 → 0.3.2
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/CHANGELOG.md +133 -0
- package/LICENSE +21 -0
- package/README.en.md +141 -0
- package/README.md +70 -76
- package/client/client.js +390 -0
- package/cordis.patch.yml +76 -5
- package/lib/core/index.js +756 -0
- package/lib/installer/detect.js +374 -0
- package/lib/installer/download.js +247 -0
- package/lib/installer/index.js +254 -0
- package/lib/mentions/index.js +595 -0
- package/lib/permissions/index.js +136 -0
- package/lib/runtime/cli.js +229 -0
- package/lib/runtime/client.js +454 -0
- package/lib/runtime/codec.js +126 -0
- package/lib/runtime/endpoint.js +248 -0
- package/lib/runtime/errors.js +126 -0
- package/lib/runtime/instances.js +287 -0
- package/lib/runtime/net.js +143 -0
- package/lib/runtime/peer.js +132 -0
- package/lib/tool-browser/index.js +476 -0
- package/lib/update-check.js +343 -0
- package/lib/web-fetch/index.js +219 -0
- package/package.json +55 -16
- package/skills/tabbit/SKILL.md +66 -0
- package/skills/tabbit/references/interaction-helpers.md +150 -0
- package/skills/tabbit/references/platform-invocation.md +174 -0
- package/skills/{tabbit-browser → tabbit}/references/playwright-recipes.md +11 -3
- package/skills/tabbit/references/runtime-recovery.md +104 -0
- package/README.zh-CN.md +0 -114
- package/index.js +0 -352
- package/installer.js +0 -568
- package/skills/tabbit-browser/SKILL.md +0 -274
- package/skills/tabbit-browser/agents/openai.yaml +0 -4
- package/skills/tabbit-browser/references/interaction-helpers.md +0 -103
- package/skills/tabbit-browser/references/platform-invocation.md +0 -45
- package/skills/tabbit-browser/references/runtime-recovery.md +0 -95
- package/update-check.js +0 -177
- /package/skills/{tabbit-browser → tabbit}/references/information-extraction.md +0 -0
package/update-check.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
2
|
-
import { homedir } from 'node:os'
|
|
3
|
-
import { dirname, join } from 'node:path'
|
|
4
|
-
|
|
5
|
-
const DAY_MS = 24 * 60 * 60 * 1000
|
|
6
|
-
const FETCH_TIMEOUT_MS = 1500
|
|
7
|
-
const CHANGELOG_MAX_CHARS = 500
|
|
8
|
-
// The raw CDN carries no GitHub API rate limit, which unauthenticated checks
|
|
9
|
-
// from shared egress IPs would otherwise exhaust within the hour.
|
|
10
|
-
const DEFAULT_CHANGELOG_URL = 'https://raw.githubusercontent.com/Tabbit-Browser/dsh-tabbit/main/CHANGELOG.md'
|
|
11
|
-
const PACKAGE_URL = new URL('./package.json', import.meta.url)
|
|
12
|
-
|
|
13
|
-
let cachedLocalVersion
|
|
14
|
-
|
|
15
|
-
function numericVersion(version) {
|
|
16
|
-
const match = String(version ?? '').trim().match(/^v?(\d+(?:\.\d+)*)/i)
|
|
17
|
-
return match ? match[1].split('.').map(Number) : undefined
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function compareVersions(left, right) {
|
|
21
|
-
const leftParts = numericVersion(left)
|
|
22
|
-
const rightParts = numericVersion(right)
|
|
23
|
-
if (!leftParts || !rightParts) return undefined
|
|
24
|
-
const length = Math.max(leftParts.length, rightParts.length)
|
|
25
|
-
for (let index = 0; index < length; index += 1) {
|
|
26
|
-
const a = leftParts[index] ?? 0
|
|
27
|
-
const b = rightParts[index] ?? 0
|
|
28
|
-
if (a !== b) return a > b ? 1 : -1
|
|
29
|
-
}
|
|
30
|
-
return 0
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function flattenChangelog(text) {
|
|
34
|
-
return String(text ?? '').replace(/\s+/g, ' ').trim()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function truncateChangelog(text) {
|
|
38
|
-
const value = flattenChangelog(text)
|
|
39
|
-
if (value.length <= CHANGELOG_MAX_CHARS) return value
|
|
40
|
-
return `${value.slice(0, CHANGELOG_MAX_CHARS - 1).trimEnd()}…`
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function parseLatestChangelog(markdown) {
|
|
44
|
-
const match = String(markdown ?? '').match(/^## +(v?\d+(?:\.\d+)+).*$/m)
|
|
45
|
-
if (!match) throw new Error('Latest changelog has no version heading.')
|
|
46
|
-
const version = numericVersion(match[1])?.join('.')
|
|
47
|
-
if (!version) throw new Error('Latest changelog heading has no usable version.')
|
|
48
|
-
const sectionStart = match.index + match[0].length
|
|
49
|
-
const nextSection = String(markdown).slice(sectionStart).search(/^## +/m)
|
|
50
|
-
const section = nextSection === -1
|
|
51
|
-
? String(markdown).slice(sectionStart)
|
|
52
|
-
: String(markdown).slice(sectionStart, sectionStart + nextSection)
|
|
53
|
-
return { version, changelog: truncateChangelog(section) }
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function defaultCacheFile(env = process.env, platform = process.platform) {
|
|
57
|
-
const base = env.XDG_CACHE_HOME
|
|
58
|
-
|| (platform === 'win32'
|
|
59
|
-
? env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
60
|
-
: undefined)
|
|
61
|
-
|| join(homedir(), '.cache')
|
|
62
|
-
return join(base, 'tabbit-dsh', 'update-check.json')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function readLocalVersion() {
|
|
66
|
-
if (cachedLocalVersion !== undefined) return cachedLocalVersion ?? undefined
|
|
67
|
-
try {
|
|
68
|
-
const parsed = JSON.parse(await readFile(PACKAGE_URL, 'utf8'))
|
|
69
|
-
cachedLocalVersion = typeof parsed.version === 'string' ? parsed.version : null
|
|
70
|
-
} catch {
|
|
71
|
-
cachedLocalVersion = null
|
|
72
|
-
}
|
|
73
|
-
return cachedLocalVersion ?? undefined
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function readCachedCheck(cacheFile) {
|
|
77
|
-
try {
|
|
78
|
-
const parsed = JSON.parse(await readFile(cacheFile, 'utf8'))
|
|
79
|
-
return parsed && typeof parsed === 'object' ? parsed : {}
|
|
80
|
-
} catch {
|
|
81
|
-
return {}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function writeCachedCheck(cacheFile, state) {
|
|
86
|
-
await mkdir(dirname(cacheFile), { recursive: true })
|
|
87
|
-
await writeFile(cacheFile, `${JSON.stringify(state, null, 2)}\n`, 'utf8')
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export async function fetchLatestChangelog({
|
|
91
|
-
url = process.env.TABBIT_PLUGIN_UPDATE_URL || DEFAULT_CHANGELOG_URL,
|
|
92
|
-
timeoutMs = FETCH_TIMEOUT_MS,
|
|
93
|
-
fetchImpl = fetch,
|
|
94
|
-
} = {}) {
|
|
95
|
-
const controller = new AbortController()
|
|
96
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs)
|
|
97
|
-
try {
|
|
98
|
-
const response = await fetchImpl(url, { signal: controller.signal })
|
|
99
|
-
if (!response.ok) throw new Error(`Update check failed with HTTP ${response.status}.`)
|
|
100
|
-
return parseLatestChangelog(await response.text())
|
|
101
|
-
} finally {
|
|
102
|
-
clearTimeout(timer)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function isRecent(timestamp, now) {
|
|
107
|
-
return typeof timestamp === 'number' && now - timestamp < DAY_MS
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function summarizeUpdate({
|
|
111
|
-
currentVersion,
|
|
112
|
-
latestVersion,
|
|
113
|
-
changelog,
|
|
114
|
-
dismissedVersion,
|
|
115
|
-
}) {
|
|
116
|
-
if (!currentVersion || !latestVersion) return { status: 'unknown', currentVersion }
|
|
117
|
-
if (compareVersions(latestVersion, currentVersion) !== 1 || latestVersion === dismissedVersion) {
|
|
118
|
-
return { status: 'current', currentVersion, latestVersion }
|
|
119
|
-
}
|
|
120
|
-
return { status: 'update-available', currentVersion, latestVersion, changelog }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function summaryFromCache(currentVersion, cached) {
|
|
124
|
-
return summarizeUpdate({
|
|
125
|
-
currentVersion,
|
|
126
|
-
latestVersion: cached.latestVersion,
|
|
127
|
-
changelog: cached.changelog,
|
|
128
|
-
dismissedVersion: cached.dismissedVersion,
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function fetchAndCacheRelease({ currentVersion, cached, cacheFile, now, fetchRelease }) {
|
|
133
|
-
const state = { ...cached, lastAttemptAt: now }
|
|
134
|
-
try {
|
|
135
|
-
const release = await fetchRelease()
|
|
136
|
-
state.checkedAt = now
|
|
137
|
-
state.latestVersion = release.version
|
|
138
|
-
state.changelog = release.changelog
|
|
139
|
-
} catch {
|
|
140
|
-
// Keep lastAttemptAt so the failure stays silent for a day before retrying.
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
await writeCachedCheck(cacheFile, state)
|
|
144
|
-
} catch {
|
|
145
|
-
// A read-only cache must not break the check itself.
|
|
146
|
-
}
|
|
147
|
-
return summaryFromCache(currentVersion, state)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export async function checkPluginUpdate({
|
|
151
|
-
now = Date.now(),
|
|
152
|
-
cacheFile = defaultCacheFile(),
|
|
153
|
-
fetchRelease = fetchLatestChangelog,
|
|
154
|
-
readVersion = readLocalVersion,
|
|
155
|
-
force = false,
|
|
156
|
-
} = {}) {
|
|
157
|
-
const currentVersion = await readVersion()
|
|
158
|
-
const cached = await readCachedCheck(cacheFile)
|
|
159
|
-
if (!force) {
|
|
160
|
-
if (isRecent(cached.checkedAt, now) && cached.latestVersion) {
|
|
161
|
-
return summaryFromCache(currentVersion, cached)
|
|
162
|
-
}
|
|
163
|
-
if (isRecent(cached.lastAttemptAt, now)) {
|
|
164
|
-
return { status: 'unknown', currentVersion }
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return fetchAndCacheRelease({ currentVersion, cached, cacheFile, now, fetchRelease })
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export async function dismissUpdate(version, {
|
|
171
|
-
cacheFile = defaultCacheFile(),
|
|
172
|
-
} = {}) {
|
|
173
|
-
const cached = await readCachedCheck(cacheFile)
|
|
174
|
-
const state = { ...cached, dismissedVersion: String(version ?? '').trim() }
|
|
175
|
-
await writeCachedCheck(cacheFile, state)
|
|
176
|
-
return state
|
|
177
|
-
}
|
|
File without changes
|