dsh-reference-anything 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.
Files changed (66) hide show
  1. package/LICENSE +22 -0
  2. package/NOTICE.md +54 -0
  3. package/README.md +228 -0
  4. package/cordis.patch.yml +63 -0
  5. package/images/logo.png +0 -0
  6. package/lib/client/index.d.ts +3 -0
  7. package/lib/client.js +18151 -0
  8. package/lib/contract.d.ts +300 -0
  9. package/lib/contract.js +102 -0
  10. package/lib/errors.d.ts +56 -0
  11. package/lib/errors.js +21 -0
  12. package/lib/host.d.ts +102 -0
  13. package/lib/host.js +55 -0
  14. package/lib/index.d.ts +83 -0
  15. package/lib/index.js +153 -0
  16. package/lib/opencli.d.ts +116 -0
  17. package/lib/opencli.js +356 -0
  18. package/lib/reference.d.ts +49 -0
  19. package/lib/reference.js +228 -0
  20. package/lib/render.d.ts +101 -0
  21. package/lib/render.js +250 -0
  22. package/lib/retain.d.ts +35 -0
  23. package/lib/retain.js +116 -0
  24. package/lib/search.d.ts +75 -0
  25. package/lib/search.js +126 -0
  26. package/lib/serialize.d.ts +22 -0
  27. package/lib/serialize.js +24 -0
  28. package/lib/sources/file.d.ts +100 -0
  29. package/lib/sources/file.js +236 -0
  30. package/lib/sources/web-chat/index.d.ts +114 -0
  31. package/lib/sources/web-chat/index.js +359 -0
  32. package/lib/store/spec.d.ts +312 -0
  33. package/lib/store/spec.js +94 -0
  34. package/lib/store/store.d.ts +189 -0
  35. package/lib/store/store.js +638 -0
  36. package/lib/sync/index.d.ts +91 -0
  37. package/lib/sync/index.js +425 -0
  38. package/lib/tool.d.ts +62 -0
  39. package/lib/tool.js +364 -0
  40. package/lib/typert.d.ts +2 -0
  41. package/lib/typert.js +34 -0
  42. package/lib/types.d.ts +215 -0
  43. package/lib/types.js +12 -0
  44. package/lib/uri-codec.d.ts +5 -0
  45. package/lib/uri-codec.js +39 -0
  46. package/lib/uri.d.ts +59 -0
  47. package/lib/uri.js +86 -0
  48. package/lib/web.d.ts +4 -0
  49. package/lib/web.js +11 -0
  50. package/lib/window.d.ts +23 -0
  51. package/lib/window.js +35 -0
  52. package/lib/wire.d.ts +117 -0
  53. package/lib/wire.js +49 -0
  54. package/lib/workspace.d.ts +18 -0
  55. package/lib/workspace.js +81 -0
  56. package/opencli-plugin/chatgpt.js +125 -0
  57. package/opencli-plugin/claude.js +134 -0
  58. package/opencli-plugin/common.js +307 -0
  59. package/opencli-plugin/deepseek.js +152 -0
  60. package/opencli-plugin/gemini.js +165 -0
  61. package/opencli-plugin/grok.js +123 -0
  62. package/opencli-plugin/kimi.js +123 -0
  63. package/opencli-plugin/opencli-plugin.json +6 -0
  64. package/opencli-plugin/package.json +8 -0
  65. package/opencli-plugin/since-guard.js +45 -0
  66. package/package.json +180 -0
@@ -0,0 +1,165 @@
1
+ import { domFallbackScript, registerProvider, sinceGuardSource } from './common.js'
2
+ // OpenCLI discovery marker: registerProvider below performs the cli() calls.
3
+
4
+ export const timestampSource = String.raw`value => {
5
+ if (Array.isArray(value)) {
6
+ if (value.length === 0) return ''
7
+ const seconds = Number(value[0])
8
+ const nanos = Number(value[1] || 0)
9
+ if (Number.isFinite(seconds) && seconds > 0 && seconds < 1e11) {
10
+ const date = new Date(seconds * 1000 + (Number.isFinite(nanos) ? nanos / 1e6 : 0))
11
+ return Number.isNaN(date.getTime()) ? '' : date.toISOString()
12
+ }
13
+ value = value[0]
14
+ }
15
+ if (value === undefined || value === null || value === '') return ''
16
+ if (typeof value === 'string' && Number.isNaN(Number(value))) {
17
+ const parsed = Date.parse(value)
18
+ return Number.isNaN(parsed) ? '' : new Date(parsed).toISOString()
19
+ }
20
+ const numeric = Number(value)
21
+ if (!Number.isFinite(numeric) || numeric <= 0) return ''
22
+ const milliseconds = numeric < 1e11 ? numeric * 1000
23
+ : numeric < 1e14 ? numeric : numeric < 1e17 ? numeric / 1000 : numeric / 1e6
24
+ const date = new Date(milliseconds)
25
+ return Number.isNaN(date.getTime()) ? '' : date.toISOString()
26
+ }`
27
+
28
+ const helpers = String.raw`
29
+ const toIso = (${timestampSource})
30
+ const auth = () => {
31
+ const wiz = window.__WIZ_global_data || window.WIZ_global_data || {}
32
+ if (wiz.SNlM0e) return String(wiz.SNlM0e)
33
+ for (const script of document.scripts) {
34
+ const match = (script.textContent || '').match(/"SNlM0e"\s*:\s*"([^"]+)"/)
35
+ if (match) return match[1]
36
+ }
37
+ return ''
38
+ }
39
+ const sid = () => {
40
+ const entries = performance.getEntriesByType('resource') || []
41
+ for (let index = entries.length - 1; index >= 0; index--) {
42
+ try {
43
+ const value = new URL(entries[index].name).searchParams.get('f.sid')
44
+ if (value) return value
45
+ } catch {}
46
+ }
47
+ return ''
48
+ }
49
+ const rpc = async (id, sourcePath, args) => {
50
+ const token = auth()
51
+ if (!token) return { code: 'AUTH' }
52
+ const query = new URLSearchParams({ rpcids: id, 'source-path': sourcePath,
53
+ 'f.sid': sid(), _reqid: String(Math.floor(Math.random() * 100000)), rt: 'c' })
54
+ const body = new URLSearchParams({ 'f.req': JSON.stringify([[[id, JSON.stringify(args), null, 'generic']]]), at: token })
55
+ const response = await fetch('/_/BardChatUi/data/batchexecute?' + query, {
56
+ method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-Same-Domain': '1' }, body,
57
+ })
58
+ if (response.status === 401 || response.status === 403) return { code: 'AUTH' }
59
+ if (response.status === 429) return { code: 'RATE_LIMIT' }
60
+ if (!response.ok) return { error: 'batchexecute HTTP ' + response.status }
61
+ const text = await response.text()
62
+ for (const line of text.split('\n')) {
63
+ const at = line.indexOf('[[')
64
+ if (at < 0) continue
65
+ try {
66
+ const outer = JSON.parse(line.slice(at))
67
+ for (const entry of outer) if (Array.isArray(entry) && entry[0] === 'wrb.fr' && entry[1] === id && typeof entry[2] === 'string') return { payload: JSON.parse(entry[2]) }
68
+ } catch {}
69
+ }
70
+ return { error: 'batchexecute returned no typed payload' }
71
+ }
72
+ `
73
+
74
+ const historyScript = String.raw`async function (args) {
75
+ ${helpers}
76
+ const stopEarly = (${sinceGuardSource})(args && args.since)
77
+ const rows = []
78
+ const byId = new Map()
79
+ let complete = true
80
+ try {
81
+ for (const mode of [1, 0]) {
82
+ let cursor = null
83
+ const seen = new Set()
84
+ for (let page = 0; page < 200; page++) {
85
+ const result = await rpc('MaZiqc', '/app', [100, cursor, [mode, null, 1]])
86
+ if (result.code) return JSON.stringify({ ok: false, code: result.code })
87
+ if (!Array.isArray(result.payload)) { complete = false; break }
88
+ const pageRows = []
89
+ const walk = value => {
90
+ if (!Array.isArray(value)) return
91
+ if (typeof value[0] === 'string' && /^c_[A-Za-z0-9_-]+$/.test(value[0]) && typeof (value[1] || value[2]) === 'string') {
92
+ const id = value[0].slice(2)
93
+ if (!byId.has(id)) {
94
+ const row = { provider: 'gemini', accountScope: '', id,
95
+ title: String(value[1] || value[2]), url: location.origin + '/app/' + encodeURIComponent(id),
96
+ createdAt: '', updatedAt: toIso(value[5]), messageCount: 0, cursor: '', partial: false }
97
+ byId.set(id, row)
98
+ pageRows.push(row)
99
+ }
100
+ }
101
+ for (const child of value) walk(child)
102
+ }
103
+ walk(result.payload)
104
+ const nested = Array.isArray(result.payload[0]) ? result.payload[0] : []
105
+ const next = typeof result.payload[1] === 'string' ? result.payload[1] : typeof nested[1] === 'string' ? nested[1] : ''
106
+ if (!next) break
107
+ if (stopEarly(pageRows)) break
108
+ if (seen.has(next)) { complete = false; break }
109
+ seen.add(next); cursor = next
110
+ }
111
+ }
112
+ rows.push(...byId.values())
113
+ return JSON.stringify({ ok: true, rows: rows.map(row => ({ ...row, partial: !complete })) })
114
+ } catch (error) {
115
+ const links = Array.from(document.querySelectorAll('a[href*="/app/"]'))
116
+ for (const link of links) {
117
+ const match = link.href.match(/\/app\/([A-Za-z0-9_-]+)/)
118
+ if (!match || byId.has(match[1])) continue
119
+ byId.set(match[1], true)
120
+ rows.push({ provider: 'gemini', accountScope: '', id: match[1], title: (link.textContent || '').trim() || 'Untitled Gemini chat',
121
+ url: link.href, createdAt: '', updatedAt: '', messageCount: 0, cursor: '', partial: true })
122
+ }
123
+ return JSON.stringify({ ok: rows.length > 0, rows, message: String(error && error.message || error) })
124
+ }
125
+ }`
126
+
127
+ const detailScript = String.raw`async function (args) {
128
+ ${helpers}
129
+ try {
130
+ const id = String(args.id).replace(/^c_/, '')
131
+ const result = await rpc('hNvQHb', '/app/' + id, ['c_' + id, 1000, null, 1, [1], [4], null, 1])
132
+ if (result.code) return JSON.stringify({ ok: false, code: result.code })
133
+ const turns = result.payload && result.payload[0]
134
+ if (!Array.isArray(turns)) return JSON.stringify({ ok: false, message: result.error || 'Gemini detail payload missing turns' })
135
+ const rows = []
136
+ for (const turn of turns.slice().reverse()) {
137
+ if (!Array.isArray(turn)) continue
138
+ const stamp = turn && turn[4]
139
+ const createdAt = toIso(stamp)
140
+ const responseId = turn && turn[0] && typeof turn[0][1] === 'string' ? turn[0][1] : String(rows.length)
141
+ const user = turn && turn[2] && turn[2][0] && turn[2][0][0]
142
+ const assistant = turn && turn[3] && turn[3][0] && turn[3][0][0] && turn[3][0][0][1] && turn[3][0][0][1][0]
143
+ if (typeof user === 'string' && user.trim()) rows.push({ conversationId: id, ordinal: rows.length,
144
+ messageId: responseId + '-user', parentId: '', branchId: responseId, activeBranch: true,
145
+ role: 'user', text: user.trim(), createdAt, attachmentsJson: '[]', partial: false })
146
+ if (typeof assistant === 'string' && assistant.trim()) rows.push({ conversationId: id, ordinal: rows.length,
147
+ messageId: responseId, parentId: responseId + '-user', branchId: responseId, activeBranch: true,
148
+ role: 'assistant', text: assistant.trim(), createdAt, attachmentsJson: '[]', partial: false })
149
+ }
150
+ return JSON.stringify({ ok: rows.length > 0, rows })
151
+ } catch (error) {
152
+ return JSON.stringify({ ok: false, message: String(error && error.message || error) })
153
+ }
154
+ }`
155
+
156
+ registerProvider({
157
+ site: 'dsh-gemini', provider: 'Gemini', domain: 'gemini.google.com', home: 'https://gemini.google.com/app',
158
+ conversationUrl: id => `https://gemini.google.com/app/${encodeURIComponent(id)}`,
159
+ whoamiScript: String.raw`async function () {
160
+ const match = location.pathname.match(/\/u\/(\d+)(?:\/|$)/); const slot = match ? match[1] : 'default'
161
+ const wiz = window.__WIZ_global_data || window.WIZ_global_data || {}; const identity = String(wiz.oPEP7c || wiz.CfO9Re || slot)
162
+ return JSON.stringify({ ok: Boolean(identity), identity })
163
+ }`,
164
+ historyScript, detailScript, fallbackScript: domFallbackScript,
165
+ })
@@ -0,0 +1,123 @@
1
+ import { domFallbackScript, registerProvider, sinceGuardSource } from './common.js'
2
+ // OpenCLI discovery marker: registerProvider below performs the cli() calls.
3
+
4
+ const historyScript = String.raw`async function (args) {
5
+ const stopEarly = (${sinceGuardSource})(args && args.since)
6
+ const rows = []
7
+ const seen = new Set()
8
+ let token = ''
9
+ try {
10
+ for (let page = 0; page < 200; page++) {
11
+ const url = '/rest/app-chat/conversations?pageSize=100' + (token ? '&pageToken=' + encodeURIComponent(token) : '')
12
+ const response = await fetch(url, { credentials: 'include', headers: { Accept: 'application/json' } })
13
+ if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
14
+ if (response.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
15
+ if (!response.ok) throw new Error('history HTTP ' + response.status)
16
+ const payload = await response.json()
17
+ const arrays = Object.values(payload || {}).filter(Array.isArray)
18
+ const pageStart = rows.length
19
+ for (const record of arrays.flat()) {
20
+ if (!record || typeof record !== 'object') continue
21
+ const id = String(record.conversationId || record.id || '')
22
+ if (!id || seen.has(id)) continue
23
+ seen.add(id)
24
+ rows.push({ provider: 'grok', accountScope: '', id,
25
+ title: String(record.title || record.name || 'Untitled Grok chat'),
26
+ url: location.origin + '/c/' + encodeURIComponent(id),
27
+ createdAt: String(record.createTime || record.createdAt || record.created_at || ''),
28
+ updatedAt: String(record.updateTime || record.updatedAt || record.updated_at || record.createTime || ''),
29
+ messageCount: Number(record.messageCount || record.message_count || 0),
30
+ cursor: String(payload.nextPageToken || ''), partial: false })
31
+ }
32
+ const next = String(payload.nextPageToken || '')
33
+ if (!next) break
34
+ if (stopEarly(rows.slice(pageStart))) break
35
+ if (next === token) throw new Error('repeated Grok cursor')
36
+ token = next
37
+ }
38
+ return JSON.stringify({ ok: true, rows })
39
+ } catch (error) {
40
+ const links = Array.from(document.querySelectorAll('a[href*="/c/"]'))
41
+ for (const link of links) {
42
+ const match = link.href.match(/\/c\/([^/?#]+)/)
43
+ if (!match || seen.has(match[1])) continue
44
+ seen.add(match[1])
45
+ rows.push({ provider: 'grok', accountScope: '', id: match[1],
46
+ title: (link.textContent || '').trim() || 'Untitled Grok chat', url: link.href,
47
+ createdAt: '', updatedAt: '', messageCount: 0, cursor: '', partial: true })
48
+ }
49
+ return JSON.stringify({ ok: rows.length > 0, rows, message: String(error && error.message || error) })
50
+ }
51
+ }`
52
+
53
+ const detailScript = String.raw`async function (args) {
54
+ try {
55
+ const init = { credentials: 'include', headers: { Accept: 'application/json' } }
56
+ const detailResponse = await fetch('/rest/app-chat/conversations_v2/' + encodeURIComponent(args.id), init)
57
+ if (detailResponse.status === 401 || detailResponse.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
58
+ if (detailResponse.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
59
+ if (!detailResponse.ok) throw new Error('detail HTTP ' + detailResponse.status)
60
+ const detailPayload = await detailResponse.json()
61
+ const detail = detailPayload.conversation || detailPayload
62
+ const nodeResponse = await fetch('/rest/app-chat/conversations/' + encodeURIComponent(args.id) + '/response-node', init)
63
+ if (nodeResponse.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
64
+ if (!nodeResponse.ok) throw new Error('response-node HTTP ' + nodeResponse.status)
65
+ const nodesPayload = await nodeResponse.json()
66
+ const nodes = Array.isArray(nodesPayload.responseNodes) ? nodesPayload.responseNodes : []
67
+ const ids = Array.from(new Set(nodes.map(node => String(node.responseId || '')).filter(Boolean)))
68
+ if (!ids.length) throw new Error('response-node returned no ids')
69
+ const messagesResponse = await fetch('/rest/app-chat/conversations/' + encodeURIComponent(args.id) + '/load-responses', {
70
+ ...init, method: 'POST', headers: { ...init.headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ responseIds: ids }),
71
+ })
72
+ if (messagesResponse.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
73
+ if (!messagesResponse.ok) throw new Error('load-responses HTTP ' + messagesResponse.status)
74
+ const messagesPayload = await messagesResponse.json()
75
+ const records = Array.isArray(messagesPayload.responses) ? messagesPayload.responses : []
76
+ const byId = new Map(records.map(record => [String(record.responseId || ''), record]))
77
+ const rows = []
78
+ for (const responseId of ids) {
79
+ const record = byId.get(responseId)
80
+ if (!record) continue
81
+ const sender = String(record.sender || '').toLowerCase()
82
+ const role = /human|user/.test(sender) ? 'user' : /assistant|bot|grok/.test(sender) ? 'assistant' : null
83
+ const text = String(record.message || '').replace(/<grok:render[^>]*>[\s\S]*?<\/grok:render>/gi, '').trim()
84
+ if (!role || !text) continue
85
+ const files = record.attachments || record.files || []
86
+ const attachments = (Array.isArray(files) ? files : []).map((file, index) => {
87
+ const candidate = String(file.url || file.downloadUrl || '')
88
+ let locator = ''
89
+ try { if (candidate) { const url = new URL(candidate, location.origin); if (url.origin === location.origin && url.pathname !== '/') locator = url.pathname + url.search } } catch {}
90
+ const name = String(file.name || 'attachment')
91
+ const mimeType = String(file.mimeType || file.mime_type || '')
92
+ return { attachmentId: String(file.id || index), kind: mimeType.startsWith('image/') || /\.(?:gif|jpe?g|png|webp)$/i.test(name) ? 'image' : 'file', name,
93
+ mimeType, size: Number(file.size || 0),
94
+ locator, status: locator ? 'available' : 'unavailable' }
95
+ })
96
+ rows.push({ conversationId: args.id, ordinal: rows.length, messageId: responseId,
97
+ parentId: String(record.parentResponseId || ''), branchId: String(record.branchId || ''), activeBranch: true,
98
+ role, text, createdAt: String(record.createTime || ''), attachmentsJson: JSON.stringify(attachments), partial: false })
99
+ }
100
+ return JSON.stringify({ ok: rows.length > 0, rows, title: detail.title || '' })
101
+ } catch (error) {
102
+ return JSON.stringify({ ok: false, message: String(error && error.message || error) })
103
+ }
104
+ }`
105
+
106
+ export const whoamiScript = String.raw`async function () {
107
+ for (const url of ['/api/auth/session', '/rest/app-chat/users/me', '/rest/auth/me']) {
108
+ const response = await fetch(url, { credentials: 'include', headers: { Accept: 'application/json' } })
109
+ if (!response.ok) continue
110
+ const payload = await response.json()
111
+ const user = payload.session || payload.user || payload
112
+ const identity = String(user.userId || user.id || user.xUserId || user.email || '')
113
+ if (identity) return JSON.stringify({ ok: true, identity })
114
+ }
115
+ return JSON.stringify({ ok: false, code: 'AUTH' })
116
+ }`
117
+
118
+ registerProvider({
119
+ site: 'dsh-grok', provider: 'Grok', domain: 'grok.com', home: 'https://grok.com/',
120
+ conversationUrl: id => `https://grok.com/c/${encodeURIComponent(id)}`,
121
+ whoamiScript,
122
+ historyScript, detailScript, fallbackScript: domFallbackScript,
123
+ })
@@ -0,0 +1,123 @@
1
+ import { domFallbackScript, registerProvider, sinceGuardSource } from './common.js'
2
+ // OpenCLI discovery marker: registerProvider below performs the cli() calls.
3
+
4
+ const authHeaders = String.raw`() => {
5
+ const token = localStorage.getItem('access_token') || ''
6
+ return { Accept: 'application/json', 'Content-Type': 'application/json', ...(token ? { Authorization: 'Bearer ' + token } : {}) }
7
+ }`
8
+
9
+ const historyScript = String.raw`async function (args) {
10
+ const stopEarly = (${sinceGuardSource})(args && args.since)
11
+ const headers = (${authHeaders})()
12
+ const rows = []
13
+ const toDate = value => {
14
+ const numeric = Number(value)
15
+ return Number.isFinite(numeric) && numeric > 0 ? new Date(numeric < 1e12 ? numeric * 1000 : numeric).toISOString() : String(value || '')
16
+ }
17
+ try {
18
+ for (let offset = 0, page = 0; page < 500; page++, offset += 100) {
19
+ const response = await fetch('/api/chat/list', { method: 'POST', credentials: 'include', headers,
20
+ body: JSON.stringify({ offset, size: 100 }) })
21
+ if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
22
+ if (response.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
23
+ if (!response.ok) throw new Error('history HTTP ' + response.status)
24
+ const payload = await response.json()
25
+ const items = payload.items || payload.chats || payload.data && (payload.data.items || payload.data.chats) || []
26
+ const pageStart = rows.length
27
+ for (const item of items) {
28
+ const id = String(item.id || item.chat_id || '')
29
+ if (!id) continue
30
+ rows.push({ provider: 'kimi', accountScope: '', id,
31
+ title: String(item.name || item.title || 'Untitled Kimi chat'),
32
+ url: location.origin + '/chat/' + encodeURIComponent(id),
33
+ createdAt: toDate(item.created_at || item.createdAt || item.created),
34
+ updatedAt: toDate(item.updated_at || item.updatedAt || item.last_updated_at || item.created_at),
35
+ messageCount: Number(item.message_count || item.messageCount || item.messages_count || 0),
36
+ cursor: String(item.updated_at || item.updatedAt || ''), partial: false })
37
+ }
38
+ if (items.length < 100 || stopEarly(rows.slice(pageStart))) break
39
+ }
40
+ return JSON.stringify({ ok: true, rows })
41
+ } catch (error) {
42
+ const seen = new Set()
43
+ for (const link of document.querySelectorAll('a[href*="/chat/"]')) {
44
+ const match = link.href.match(/\/chat\/([0-9a-f-]{8,})/i)
45
+ if (!match || seen.has(match[1])) continue
46
+ seen.add(match[1]); rows.push({ provider: 'kimi', accountScope: '', id: match[1],
47
+ title: (link.textContent || '').trim() || 'Untitled Kimi chat', url: link.href,
48
+ createdAt: '', updatedAt: '', messageCount: 0, cursor: '', partial: true })
49
+ }
50
+ return JSON.stringify({ ok: rows.length > 0, rows, message: rows.length ? '' : String(error && error.message || error) })
51
+ }
52
+ }`
53
+
54
+ export const detailScript = String.raw`async function (args) {
55
+ const headers = (${authHeaders})()
56
+ const textOf = value => typeof value === 'string' ? value
57
+ : Array.isArray(value) ? value.map(textOf).filter(Boolean).join('\n')
58
+ : value && typeof value === 'object' ? textOf(value.text || value.content || value.value || value.output || '') : ''
59
+ const attachmentOf = (file, index) => {
60
+ if (!file || typeof file !== 'object') return null
61
+ const candidate = String(file.download_url || file.url || file.path || '')
62
+ let locator = ''
63
+ try { if (candidate) { const url = new URL(candidate, location.origin); if (url.origin === location.origin && url.pathname !== '/') locator = url.pathname + url.search } } catch {}
64
+ const name = String(file.name || file.file_name || 'attachment')
65
+ const mimeType = String(file.mime_type || file.mimeType || file.content_type || '')
66
+ return { attachmentId: String(file.id || file.file_id || index),
67
+ kind: mimeType.startsWith('image/') || /\.(?:gif|jpe?g|png|webp)$/i.test(name) ? 'image' : 'file',
68
+ name, mimeType, size: Number(file.size || file.file_size || 0), locator,
69
+ status: locator ? 'available' : 'unavailable' }
70
+ }
71
+ try {
72
+ const messages = []
73
+ let pageToken = ''
74
+ for (let page = 0; page < 500; page++) {
75
+ let response = await fetch('/apiv2/kimi.chat.v1.ChatService/ListMessages', { method: 'POST', credentials: 'include', headers,
76
+ body: JSON.stringify({ chat_id: args.id, ...(pageToken ? { pageToken } : {}) }) })
77
+ if ((response.status === 404 || response.status === 405) && page === 0) {
78
+ response = await fetch('/api/chat/' + encodeURIComponent(args.id) + '/messages', { credentials: 'include', headers })
79
+ }
80
+ if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
81
+ if (response.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
82
+ if (!response.ok) throw new Error('detail HTTP ' + response.status)
83
+ const payload = await response.json()
84
+ const items = payload.messages || payload.items || payload.data && (payload.data.messages || payload.data.items) || []
85
+ messages.push(...items)
86
+ const next = String(payload.nextPageToken || payload.next_page_token || payload.data && (payload.data.nextPageToken || payload.data.next_page_token) || '')
87
+ if (!next || next === pageToken || items.length === 0) break
88
+ pageToken = next
89
+ }
90
+ // Kimi lists newest messages first. The local mirror and every other
91
+ // adapter use chronological ordinals, so reverse only after all pages land.
92
+ messages.reverse()
93
+ const rows = messages.map((message, ordinal) => {
94
+ const roleRaw = String(message.role || message.sender || message.message_role || message.type || '').toLowerCase()
95
+ const role = /assistant|bot|model|kimi/.test(roleRaw) ? 'assistant' : 'user'
96
+ const files = message.attachments || message.files || message.file_infos || []
97
+ const attachments = (Array.isArray(files) ? files : []).map(attachmentOf).filter(Boolean)
98
+ return { conversationId: args.id, ordinal,
99
+ messageId: String(message.id || message.message_id || ordinal),
100
+ parentId: String(message.parent_id || message.parentId || ''), branchId: '', activeBranch: true,
101
+ role, text: textOf(message.blocks || message.contents || message.content || message.text || message.fragments || ''),
102
+ createdAt: String(message.created_at || message.createdAt || message.timestamp || ''),
103
+ attachmentsJson: JSON.stringify(attachments), partial: false }
104
+ }).filter(row => row.text || row.attachmentsJson !== '[]')
105
+ return JSON.stringify({ ok: rows.length > 0, rows, message: rows.length ? '' : 'response contained no messages' })
106
+ } catch (error) {
107
+ return JSON.stringify({ ok: false, message: String(error && error.message || error) })
108
+ }
109
+ }`
110
+
111
+ registerProvider({
112
+ site: 'dsh-kimi', provider: 'Kimi', domain: 'kimi.com', home: 'https://www.kimi.com/',
113
+ conversationUrl: id => `https://www.kimi.com/chat/${encodeURIComponent(id)}`,
114
+ whoamiScript: String.raw`async function () {
115
+ const headers = (${authHeaders})()
116
+ const response = await fetch('/api/user', { credentials: 'include', headers })
117
+ if (response.status === 401 || response.status === 403) return JSON.stringify({ ok: false, code: 'AUTH' })
118
+ if (!response.ok) return JSON.stringify({ ok: false, message: 'user HTTP ' + response.status })
119
+ const user = await response.json(); const identity = String(user.id || user.user_id || user.email || '')
120
+ return JSON.stringify({ ok: Boolean(identity), identity, code: identity ? undefined : 'AUTH' })
121
+ }`,
122
+ historyScript, detailScript, fallbackScript: domFallbackScript,
123
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "dsh-chat-history",
3
+ "version": "0.2.0",
4
+ "opencli": ">=1.8.6",
5
+ "description": "Full-history read adapters used by dsh-reference-anything"
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "opencli-plugin-dsh-chat-history",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "peerDependencies": {
6
+ "@jackwener/opencli": ">=1.8.6"
7
+ }
8
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Early-stop helper shared by every provider's history script.
3
+ *
4
+ * Lives in its own module, free of imports, so it can be unit-tested without
5
+ * the OpenCLI registry that `common.js` pulls in.
6
+ */
7
+
8
+ /**
9
+ * Source of the guard, embedded into each provider's page script.
10
+ *
11
+ * Paging may only stop early while the provider actually returns
12
+ * conversations newest-first, which not every one of these endpoints
13
+ * documents. So the guard watches the `updatedAt` sequence it is fed and
14
+ * authorizes a stop only while that sequence has been non-increasing *and*
15
+ * the whole current page already predates the cutoff. A provider that pages
16
+ * in some other order, or that omits timestamps, walks its full history
17
+ * exactly as it did before `since` existed.
18
+ */
19
+ export const sinceGuardSource = String.raw`function (since) {
20
+ const cutoff = since ? Date.parse(since) : NaN
21
+ let previous = Infinity
22
+ let ordered = true
23
+ return function (page) {
24
+ if (!(cutoff > 0) || page.length === 0) return false
25
+ let older = true
26
+ for (const row of page) {
27
+ const at = Date.parse(row.updatedAt || '')
28
+ if (!(at > 0)) return false
29
+ if (at > previous) ordered = false
30
+ previous = at
31
+ if (at >= cutoff) older = false
32
+ }
33
+ return ordered && older
34
+ }
35
+ }`
36
+
37
+ /**
38
+ * Materialize the guard for tests and for any caller that wants it directly.
39
+ * @param {string} since - ISO instant, or '' to disable early stopping.
40
+ * @returns {(page: Array<{updatedAt?: string}>) => boolean} whether paging may stop.
41
+ */
42
+ export function createSinceGuard(since) {
43
+ // eslint-disable-next-line no-eval -- the source is a package constant, never caller data.
44
+ return (0, eval)(`(${sinceGuardSource})`)(since)
45
+ }
package/package.json ADDED
@@ -0,0 +1,180 @@
1
+ {
2
+ "name": "dsh-reference-anything",
3
+ "version": "0.2.0",
4
+ "description": "Reference locally mirrored ChatGPT, Claude, Gemini, DeepSeek, Grok, and Kimi conversations from DeepSeek Harness",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Chael-Chael/dsh-reference-anything.git"
13
+ },
14
+ "main": "lib/index.js",
15
+ "types": "lib/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./reference": {
22
+ "types": "./lib/reference.d.ts",
23
+ "default": "./lib/reference.js"
24
+ },
25
+ "./tool": {
26
+ "types": "./lib/tool.d.ts",
27
+ "default": "./lib/tool.js"
28
+ },
29
+ "./file": {
30
+ "types": "./lib/sources/file.d.ts",
31
+ "default": "./lib/sources/file.js"
32
+ },
33
+ "./web-chat": {
34
+ "types": "./lib/sources/web-chat/index.d.ts",
35
+ "default": "./lib/sources/web-chat/index.js"
36
+ },
37
+ "./web": {
38
+ "types": "./lib/web.d.ts",
39
+ "default": "./lib/web.js"
40
+ },
41
+ "./sync": {
42
+ "types": "./lib/sync/index.d.ts",
43
+ "default": "./lib/sync/index.js"
44
+ },
45
+ "./client": {
46
+ "types": "./lib/client/index.d.ts",
47
+ "default": "./lib/client.js"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "files": [
52
+ "lib",
53
+ "opencli-plugin/*.js",
54
+ "opencli-plugin/opencli-plugin.json",
55
+ "opencli-plugin/package.json",
56
+ "cordis.patch.yml",
57
+ "images/logo.png",
58
+ "NOTICE.md",
59
+ "README.md"
60
+ ],
61
+ "dsh": {
62
+ "bundle": {
63
+ "patch": "./cordis.patch.yml"
64
+ },
65
+ "client": {
66
+ "platform": "web",
67
+ "inject": [
68
+ "@deepseek-ai/dsh-client-runtime",
69
+ "@deepseek-ai/dsh-api-remotes",
70
+ "@deepseek-ai/dsh-client-ui-input-trigger",
71
+ "@deepseek-ai/dsh-client-ui-conversation",
72
+ "@deepseek-ai/dsh-client-ui-settings",
73
+ "@deepseek-ai/dsh-client-locale"
74
+ ]
75
+ }
76
+ },
77
+ "scripts": {
78
+ "build": "node build.mjs",
79
+ "typecheck": "tsc -p tsconfig.json --noEmit",
80
+ "test": "vitest run",
81
+ "check": "pnpm run typecheck && pnpm run test && pnpm run build",
82
+ "prepublishOnly": "pnpm run check",
83
+ "prepare": "node build.mjs"
84
+ },
85
+ "peerDependencies": {
86
+ "@deepseek-ai/cordis": ">=4.0.1",
87
+ "@deepseek-ai/dsh-agent": ">=0.1.0-rc.6",
88
+ "@deepseek-ai/dsh-api-remotes": ">=0.0.1-rc.1",
89
+ "@deepseek-ai/dsh-attachment": ">=0.1.0-rc.6",
90
+ "@deepseek-ai/dsh-client-connection": ">=0.0.1-rc.1",
91
+ "@deepseek-ai/dsh-client-locale": ">=0.0.1-rc.1",
92
+ "@deepseek-ai/dsh-client-runtime": ">=0.0.1-rc.1",
93
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.0.1-rc.1",
94
+ "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.0.1-rc.3",
95
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.0.1-rc.1",
96
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.0.1-rc.1",
97
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.6",
98
+ "@deepseek-ai/dsh-output-retention": ">=0.1.0-rc.6",
99
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.6",
100
+ "@deepseek-ai/dsh-session-reference": ">=0.1.0-rc.6",
101
+ "@deepseek-ai/dsh-settings": ">=0.0.1-rc.1",
102
+ "@deepseek-ai/dsh-storage-domain": ">=0.0.1-rc.1",
103
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.6",
104
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.0-rc.6",
105
+ "@deepseek-ai/dsh-typert-registry": ">=0.0.1-rc.1",
106
+ "react": "^18.2.0"
107
+ },
108
+ "peerDependenciesMeta": {
109
+ "@deepseek-ai/dsh-session-reference": {
110
+ "optional": true
111
+ },
112
+ "@deepseek-ai/dsh-client-runtime": {
113
+ "optional": true
114
+ },
115
+ "@deepseek-ai/dsh-api-remotes": {
116
+ "optional": true
117
+ },
118
+ "@deepseek-ai/dsh-client-ui-input-trigger": {
119
+ "optional": true
120
+ },
121
+ "@deepseek-ai/dsh-client-ui-slots": {
122
+ "optional": true
123
+ },
124
+ "@deepseek-ai/dsh-client-ui-conversation": {
125
+ "optional": true
126
+ },
127
+ "@deepseek-ai/dsh-client-locale": {
128
+ "optional": true
129
+ },
130
+ "@deepseek-ai/dsh-client-connection": {
131
+ "optional": true
132
+ },
133
+ "@deepseek-ai/dsh-client-ui-settings": {
134
+ "optional": true
135
+ },
136
+ "@deepseek-ai/dsh-settings": {
137
+ "optional": true
138
+ },
139
+ "react": {
140
+ "optional": true
141
+ }
142
+ },
143
+ "dependencies": {
144
+ "@deepseek-ai/schemastery": "^3.18.1",
145
+ "@lobehub/icons": "^5.16.0",
146
+ "zod": "^4.4.3"
147
+ },
148
+ "devDependencies": {
149
+ "@deepseek-ai/cordis": "link:../deepseek-harness/vendor/cordis",
150
+ "@deepseek-ai/dsh-agent": "link:../deepseek-harness/packages/core/agent",
151
+ "@deepseek-ai/dsh-api-remotes": "link:../deepseek-harness/packages/api/remotes",
152
+ "@deepseek-ai/dsh-attachment": "link:../deepseek-harness/packages/attachment/attachment",
153
+ "@deepseek-ai/dsh-client-connection": "link:../deepseek-harness/packages/client/connection",
154
+ "@deepseek-ai/dsh-client-locale": "link:../deepseek-harness/packages/client/locale",
155
+ "@deepseek-ai/dsh-client-runtime": "link:../deepseek-harness/packages/client/runtime",
156
+ "@deepseek-ai/dsh-client-ui-conversation": "link:../deepseek-harness/packages/client/ui-conversation",
157
+ "@deepseek-ai/dsh-client-ui-input-trigger": "link:../deepseek-harness/packages/client/ui-input-trigger",
158
+ "@deepseek-ai/dsh-client-ui-settings": "link:../deepseek-harness/packages/client/ui-settings",
159
+ "@deepseek-ai/dsh-client-ui-slots": "link:../deepseek-harness/packages/client/ui-slots",
160
+ "@deepseek-ai/dsh-llm": "link:../deepseek-harness/packages/llm/llm",
161
+ "@deepseek-ai/dsh-output-retention": "link:../deepseek-harness/packages/util/output-retention",
162
+ "@deepseek-ai/dsh-session": "link:../deepseek-harness/packages/core/session",
163
+ "@deepseek-ai/dsh-session-reference": "link:../deepseek-harness/packages/context/session-reference",
164
+ "@deepseek-ai/dsh-settings": "link:../deepseek-harness/packages/settings/settings",
165
+ "@deepseek-ai/dsh-storage-domain": "link:../deepseek-harness/packages/storage/storage-domain",
166
+ "@deepseek-ai/dsh-system-prompt": "link:../deepseek-harness/packages/core/system-prompt",
167
+ "@deepseek-ai/dsh-tools": "link:../deepseek-harness/packages/core/tools",
168
+ "@deepseek-ai/dsh-typert-protocol": "link:../deepseek-harness/packages/typert/protocol",
169
+ "@deepseek-ai/dsh-typert-registry": "link:../deepseek-harness/packages/typert/registry",
170
+ "@types/node": "^22.20.1",
171
+ "@types/react": "^18.2.0",
172
+ "@types/react-dom": "^18.3.7",
173
+ "esbuild": "^0.25.0",
174
+ "jsdom": "^26.1.0",
175
+ "react": "^18.2.0",
176
+ "react-dom": "^18.2.0",
177
+ "typescript": "^5.7.0",
178
+ "vitest": "^3.0.0"
179
+ }
180
+ }