dsh-reference-anything 0.5.1 → 0.5.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/LICENSE +22 -22
- package/NOTICE.md +110 -110
- package/README.md +472 -459
- package/cordis.patch.yml +136 -136
- package/docs/release/0.4.4/README.md +75 -0
- package/{release-notes → docs/release}/0.4.4/features.json +36 -36
- package/docs/release/0.5.0/README.md +148 -0
- package/{release-notes → docs/release}/0.5.0/features.json +65 -65
- package/docs/release/0.5.1/README.md +33 -0
- package/docs/release/0.5.2/README.md +51 -0
- package/docs/release/0.5.2/features.json +29 -0
- package/{release-notes → docs/release}/README.md +42 -38
- package/images/agents/LICENSE +22 -22
- package/images/agents/README.md +7 -3
- package/images/agents/antigravity.svg +1 -1
- package/images/agents/cline.svg +1 -1
- package/images/agents/continue.svg +1 -1
- package/images/agents/copilot.svg +27 -0
- package/images/agents/crush.svg +1 -1
- package/images/agents/doubao.svg +1 -0
- package/images/agents/goose.svg +1 -1
- package/images/agents/omp.svg +1 -0
- package/images/agents/reasonix.svg +5 -5
- package/images/agents/teleagent.svg +1 -1
- package/images/agents/trae.svg +1 -1
- package/images/agents/vibe.svg +1 -1
- package/images/agents/workbuddy.svg +37 -37
- package/images/agents/zed.svg +1 -1
- package/lib/chat-import-compatibility.js +8 -5
- package/lib/client.js +36526 -4
- package/lib/contract.d.ts +4 -0
- package/lib/host.d.ts +2 -2
- package/lib/local-conversation-reader.js +16 -11
- package/lib/render.js +21 -21
- package/lib/store/spec.d.ts +9 -1
- package/lib/tool.js +3 -2
- package/lib/wire.d.ts +13 -1
- package/lib/wire.js +5 -4
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/opencli-plugin/chatgpt.js +128 -128
- package/opencli-plugin/claude.js +138 -138
- package/opencli-plugin/common.js +452 -452
- package/opencli-plugin/deepseek.js +155 -155
- package/opencli-plugin/gemini.js +169 -169
- package/opencli-plugin/grok.js +124 -124
- package/opencli-plugin/kimi.js +126 -126
- package/opencli-plugin/opencli-plugin.json +6 -6
- package/opencli-plugin/package.json +8 -8
- package/opencli-plugin/since-guard.js +45 -45
- package/package.json +263 -262
- package/vendor/chat-import-reader/LICENSE +22 -22
- package/vendor/chat-import-reader/LICENSE.fzstd +20 -20
- package/vendor/chat-import-reader/README.md +54 -48
- package/vendor/chat-import-reader/availability-worker.mjs +119 -114
- package/vendor/chat-import-reader/parsers.d.mts +6 -6
- package/vendor/chat-import-reader/parsers.mjs +13069 -11168
- package/vendor/chat-import-reader/scan-worker.mjs +7 -7
- package/vendor/chat-import-reader/scanners.mjs +5523 -4286
- package/vendor/chat-import-reader/upstream-sha256.json +114 -101
- package/release-notes/0.4.4/commands.png +0 -0
- package/release-notes/0.4.4/demos/agent-import.gif +0 -0
- package/release-notes/0.4.4/demos/chat-import-upgrade.gif +0 -0
- package/release-notes/0.4.4/demos/folders.gif +0 -0
- package/release-notes/0.4.4/demos/local-plugins.gif +0 -0
- package/release-notes/0.4.4/demos/shortcuts.gif +0 -0
- package/release-notes/0.4.4/files.png +0 -0
- package/release-notes/0.5.0/demos/agent-import.gif +0 -0
- package/release-notes/0.5.0/demos/chat-import-upgrade.gif +0 -0
- package/release-notes/0.5.0/demos/folders.gif +0 -0
- package/release-notes/0.5.0/demos/local-plugins.gif +0 -0
- package/release-notes/0.5.0/demos/local-references.gif +0 -0
- package/release-notes/0.5.0/demos/shortcuts.gif +0 -0
package/opencli-plugin/gemini.js
CHANGED
|
@@ -1,169 +1,169 @@
|
|
|
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
|
-
export const whoamiScript = String.raw`async function () {
|
|
157
|
-
const wiz = window.__WIZ_global_data || window.WIZ_global_data || {}
|
|
158
|
-
const candidate = wiz.oPEP7c || wiz.CfO9Re
|
|
159
|
-
const identity = typeof candidate === 'string' ? candidate.trim() : ''
|
|
160
|
-
if (!identity) return JSON.stringify({ ok: false, message: 'Gemini stable account identity unavailable' })
|
|
161
|
-
return JSON.stringify({ ok: true, identity })
|
|
162
|
-
}`
|
|
163
|
-
|
|
164
|
-
registerProvider({
|
|
165
|
-
site: 'dsh-gemini', provider: 'Gemini', domain: 'gemini.google.com', home: 'https://gemini.google.com/app',
|
|
166
|
-
conversationUrl: id => `https://gemini.google.com/app/${encodeURIComponent(id)}`,
|
|
167
|
-
whoamiScript,
|
|
168
|
-
historyScript, detailScript, fallbackScript: domFallbackScript,
|
|
169
|
-
})
|
|
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
|
+
export const whoamiScript = String.raw`async function () {
|
|
157
|
+
const wiz = window.__WIZ_global_data || window.WIZ_global_data || {}
|
|
158
|
+
const candidate = wiz.oPEP7c || wiz.CfO9Re
|
|
159
|
+
const identity = typeof candidate === 'string' ? candidate.trim() : ''
|
|
160
|
+
if (!identity) return JSON.stringify({ ok: false, message: 'Gemini stable account identity unavailable' })
|
|
161
|
+
return JSON.stringify({ ok: true, identity })
|
|
162
|
+
}`
|
|
163
|
+
|
|
164
|
+
registerProvider({
|
|
165
|
+
site: 'dsh-gemini', provider: 'Gemini', domain: 'gemini.google.com', home: 'https://gemini.google.com/app',
|
|
166
|
+
conversationUrl: id => `https://gemini.google.com/app/${encodeURIComponent(id)}`,
|
|
167
|
+
whoamiScript,
|
|
168
|
+
historyScript, detailScript, fallbackScript: domFallbackScript,
|
|
169
|
+
})
|
package/opencli-plugin/grok.js
CHANGED
|
@@ -1,124 +1,124 @@
|
|
|
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.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
|
|
110
|
-
if (!response.ok) continue
|
|
111
|
-
const payload = await response.json()
|
|
112
|
-
const user = payload.session || payload.user || payload
|
|
113
|
-
const identity = String(user.userId || user.id || user.xUserId || user.email || '')
|
|
114
|
-
if (identity) return JSON.stringify({ ok: true, identity })
|
|
115
|
-
}
|
|
116
|
-
return JSON.stringify({ ok: false, code: 'AUTH' })
|
|
117
|
-
}`
|
|
118
|
-
|
|
119
|
-
registerProvider({
|
|
120
|
-
site: 'dsh-grok', provider: 'Grok', domain: 'grok.com', home: 'https://grok.com/',
|
|
121
|
-
conversationUrl: id => `https://grok.com/c/${encodeURIComponent(id)}`,
|
|
122
|
-
whoamiScript,
|
|
123
|
-
historyScript, detailScript, fallbackScript: domFallbackScript,
|
|
124
|
-
})
|
|
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.status === 429) return JSON.stringify({ ok: false, code: 'RATE_LIMIT' })
|
|
110
|
+
if (!response.ok) continue
|
|
111
|
+
const payload = await response.json()
|
|
112
|
+
const user = payload.session || payload.user || payload
|
|
113
|
+
const identity = String(user.userId || user.id || user.xUserId || user.email || '')
|
|
114
|
+
if (identity) return JSON.stringify({ ok: true, identity })
|
|
115
|
+
}
|
|
116
|
+
return JSON.stringify({ ok: false, code: 'AUTH' })
|
|
117
|
+
}`
|
|
118
|
+
|
|
119
|
+
registerProvider({
|
|
120
|
+
site: 'dsh-grok', provider: 'Grok', domain: 'grok.com', home: 'https://grok.com/',
|
|
121
|
+
conversationUrl: id => `https://grok.com/c/${encodeURIComponent(id)}`,
|
|
122
|
+
whoamiScript,
|
|
123
|
+
historyScript, detailScript, fallbackScript: domFallbackScript,
|
|
124
|
+
})
|