dsh-as-aistudio 0.2.13 → 0.3.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/CHANGELOG.md +31 -0
- package/README.md +5 -5
- package/package.json +17 -12
- package/src/client.js +3362 -225
- package/src/components.js +1 -1
- package/src/index.js +26 -1
- package/src/shell.js +30 -5
- package/src/vendor/host/_platform.js +132 -0
- package/src/vendor/host/dsh-delete-turn.js +661 -0
- package/src/vendor/host/dsh-delete-turn.logic.js +655 -0
- package/src/vendor/host/dsh-edit-turn.js +2813 -0
- package/src/vendor/host/dsh-edit-turn.patch.js +285 -0
- package/src/vendor/host/dsh-markdown-bubble.js +37 -0
- package/src/vendor/host/dsh-rerun-turn.js +2784 -0
- package/src/vendor/versions.json +6 -0
- package/docs/INTEROP.md +0 -230
- package/src/studio.js +0 -553
- package/src/vendor/dsh-delete-turn.js +0 -1174
- package/src/vendor/dsh-edit-turn.js +0 -2626
- package/src/vendor/dsh-markdown-bubble.js +0 -872
- package/src/vendor/dsh-rerun-turn.js +0 -1014
- package/tools/build.mjs +0 -87
- package/tools/bump.mjs +0 -82
- package/tools/head.txt +0 -13
- package/tools/tail.txt +0 -5
- package/tools/tail2.txt +0 -4
- package/tools/vendor.mjs +0 -125
- package/tools/verify-manifest.mjs +0 -77
package/src/components.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
export const STUDIO_ID = 'dsh-as-aistudio'
|
|
27
27
|
|
|
28
28
|
/** Keep in sync with package.json and src/client.js. */
|
|
29
|
-
export const STUDIO_VERSION = '0.
|
|
29
|
+
export const STUDIO_VERSION = '0.3.0'
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* The AI Studio action strip this studio composes, in the order the user meets
|
package/src/index.js
CHANGED
|
@@ -116,6 +116,27 @@ function safeGet(ctx, service) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* The version of the code this studio actually runs.
|
|
121
|
+
*
|
|
122
|
+
* Both halves of every component are vendored here (tools/vendor.mjs writes
|
|
123
|
+
* src/vendor/versions.json as it copies them), so a component package does not
|
|
124
|
+
* even have to be installed. Reading the version off the installed package would
|
|
125
|
+
* therefore answer "unknown" on a perfectly working studio - the exact report
|
|
126
|
+
* this function replaces.
|
|
127
|
+
*
|
|
128
|
+
* @returns {Record<string, string>} component id -> vendored version.
|
|
129
|
+
*/
|
|
130
|
+
function vendoredVersions() {
|
|
131
|
+
try {
|
|
132
|
+
const raw = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), 'vendor', 'versions.json'), 'utf8')
|
|
133
|
+
const parsed = JSON.parse(raw)
|
|
134
|
+
return parsed !== null && typeof parsed === 'object' ? parsed : {}
|
|
135
|
+
} catch {
|
|
136
|
+
return {}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
119
140
|
/**
|
|
120
141
|
* Resolve one component package from the profile.
|
|
121
142
|
* @param pkg - the bare package name.
|
|
@@ -251,6 +272,7 @@ export function hostStateOf(component, entries, installed) {
|
|
|
251
272
|
export function statusOf(ctx) {
|
|
252
273
|
const entries = loaderEntries(ctx)
|
|
253
274
|
const anchors = resolveAnchors(ctx)
|
|
275
|
+
const vendored = vendoredVersions()
|
|
254
276
|
const components = COMPONENTS.map((component) => {
|
|
255
277
|
const found = inspectPackage(component.package, anchors)
|
|
256
278
|
const host = hostStateOf(component, entries, found.installed)
|
|
@@ -265,7 +287,10 @@ export function statusOf(ctx) {
|
|
|
265
287
|
kind: component.kind,
|
|
266
288
|
repo: component.repo,
|
|
267
289
|
installed: found.installed,
|
|
268
|
-
version
|
|
290
|
+
// The version of the copy that is RUNNING. `installedVersion` is the
|
|
291
|
+
// package in the profile, which may be absent or a different build.
|
|
292
|
+
version: vendored[component.id] ?? found.version,
|
|
293
|
+
installedVersion: found.version,
|
|
269
294
|
hostMounted: host.hostMounted,
|
|
270
295
|
hostReason: host.hostReason,
|
|
271
296
|
hostRow: host.hostRow,
|
package/src/shell.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// file - and carries the real component package in `config.plugin`. The shell
|
|
7
7
|
// is what turns that name into a mount:
|
|
8
8
|
//
|
|
9
|
-
// const namespace = await import(
|
|
9
|
+
// const namespace = await import('./vendor/host/' + id + '.js')
|
|
10
10
|
// await ctx.plugin(namespace.default ?? namespace.plugin ?? namespace)
|
|
11
11
|
//
|
|
12
12
|
// Why the row is a subpath of this package and not the component's own name:
|
|
@@ -28,7 +28,13 @@
|
|
|
28
28
|
//
|
|
29
29
|
// The row config is read once per activation. Editing it in the plugin manager
|
|
30
30
|
// restarts the entry, which runs this apply again with the new value.
|
|
31
|
-
|
|
31
|
+
//
|
|
32
|
+
// The component's HOST half is vendored into this package (tools/vendor.mjs),
|
|
33
|
+
// exactly like its browser half: `config.plugin` names which component the row
|
|
34
|
+
// is, and the vendored file beside this one is what gets mounted. That is why
|
|
35
|
+
// installing the studio installs nothing else - and why a component's fix
|
|
36
|
+
// reaches the studio only through a re-vendor and a studio release.
|
|
37
|
+
import { COMPONENTS, STUDIO_ID } from './components.js'
|
|
32
38
|
import { pluginOf } from './index.js'
|
|
33
39
|
|
|
34
40
|
/** Loader display name of this shell (all four rows share it). */
|
|
@@ -64,6 +70,14 @@ export const Config = {
|
|
|
64
70
|
},
|
|
65
71
|
}
|
|
66
72
|
|
|
73
|
+
/** The component id a row's `plugin` names, or null when it names none of ours. */
|
|
74
|
+
function componentIdOf(spec) {
|
|
75
|
+
for (const component of COMPONENTS) {
|
|
76
|
+
if (component.package === spec) return component.id
|
|
77
|
+
}
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
67
81
|
/** The package name a row config names, or null when it names none. */
|
|
68
82
|
function pluginSpecOf(config) {
|
|
69
83
|
if (config === null || config === undefined || typeof config !== 'object') return null
|
|
@@ -103,11 +117,22 @@ export async function apply(ctx, config) {
|
|
|
103
117
|
STUDIO_ID + ': this component row names no plugin; set config.plugin to the component package (row config: ' + describeConfig(config) + ')',
|
|
104
118
|
)
|
|
105
119
|
}
|
|
120
|
+
const id = componentIdOf(spec)
|
|
121
|
+
if (id === null) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
STUDIO_ID +
|
|
124
|
+
': component ' +
|
|
125
|
+
JSON.stringify(spec) +
|
|
126
|
+
' is not one of this studio\'s components (' +
|
|
127
|
+
COMPONENTS.map((component) => component.package).join(', ') +
|
|
128
|
+
')',
|
|
129
|
+
)
|
|
130
|
+
}
|
|
106
131
|
let namespace
|
|
107
132
|
try {
|
|
108
|
-
// The
|
|
109
|
-
//
|
|
110
|
-
namespace = await import(
|
|
133
|
+
// The host half is vendored beside this file (tools/vendor.mjs), so the
|
|
134
|
+
// component package does not have to be installed at all.
|
|
135
|
+
namespace = await import('./vendor/host/' + id + '.js')
|
|
111
136
|
} catch (error) {
|
|
112
137
|
throw new Error(
|
|
113
138
|
STUDIO_ID + ': component ' + JSON.stringify(spec) + ' could not be imported: ' + messageOf(error),
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// The platform packages a vendored component host half may import.
|
|
2
|
+
//
|
|
3
|
+
// A component copied into this package must NOT get its own copy of
|
|
4
|
+
// `@deepseek-ai/dsh-session`: two module instances mean two registries, and the
|
|
5
|
+
// second copy's `appendPluginRecord` cannot touch a Session the host created
|
|
6
|
+
// (the brand check inside it throws). The same goes for `dsh-tools`, whose
|
|
7
|
+
// `defineTool` builds validators against schemas the host's own tool registry
|
|
8
|
+
// expects.
|
|
9
|
+
//
|
|
10
|
+
// So the copies import THIS file, and this file resolves the two packages from
|
|
11
|
+
// the running harness. The harness lives in the `dsh` installation, so the base
|
|
12
|
+
// is found by walking up from this file to the nearest node_modules that has it,
|
|
13
|
+
// with the profile's own tree as the second try. A failure here is a failure of
|
|
14
|
+
// the whole component row and says exactly which package was missing - silence
|
|
15
|
+
// would report a component that never started.
|
|
16
|
+
|
|
17
|
+
import { createRequire } from 'node:module'
|
|
18
|
+
import { existsSync } from 'node:fs'
|
|
19
|
+
import { dirname, join } from 'node:path'
|
|
20
|
+
import { fileURLToPath } from 'node:url'
|
|
21
|
+
|
|
22
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Candidate base directories, nearest-first, for resolving a platform package.
|
|
26
|
+
*
|
|
27
|
+
* Two roots, because a plugin is loaded from one tree and the harness runs from
|
|
28
|
+
* another: this file's own ancestors (a profile that installed the packages
|
|
29
|
+
* beside it), then the RUNNING process's entry point - `process.argv[1]` is the
|
|
30
|
+
* `dsh` binary, and the platform packages sit in that installation's
|
|
31
|
+
* `node_modules`. ESM resolves relative to the importing FILE, so the second
|
|
32
|
+
* root has to be built by hand.
|
|
33
|
+
*
|
|
34
|
+
* @returns directory list to try with createRequire.
|
|
35
|
+
*/
|
|
36
|
+
function bases() {
|
|
37
|
+
const out = []
|
|
38
|
+
const seen = new Set()
|
|
39
|
+
const add = (start) => {
|
|
40
|
+
if (typeof start !== 'string' || start === '') return
|
|
41
|
+
let node = start
|
|
42
|
+
for (let up = 0; up < 10; up += 1) {
|
|
43
|
+
const candidate = join(node, 'node_modules')
|
|
44
|
+
if (!seen.has(candidate) && existsSync(candidate)) {
|
|
45
|
+
seen.add(candidate)
|
|
46
|
+
out.push(candidate)
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(node)
|
|
49
|
+
if (parent === node) break
|
|
50
|
+
node = parent
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
add(here)
|
|
54
|
+
add(typeof process !== 'undefined' && process.argv ? process.argv[1] : '')
|
|
55
|
+
add(typeof process !== 'undefined' ? process.cwd() : '')
|
|
56
|
+
return out
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve one platform package and import it as ESM.
|
|
61
|
+
*
|
|
62
|
+
* @param spec - the platform package name.
|
|
63
|
+
* @returns its module namespace.
|
|
64
|
+
* @throws when no candidate base can resolve it, naming the specifier.
|
|
65
|
+
*/
|
|
66
|
+
async function importPlatform(spec) {
|
|
67
|
+
for (const base of bases()) {
|
|
68
|
+
try {
|
|
69
|
+
const url = createRequire(join(base, 'noop.js')).resolve(spec)
|
|
70
|
+
return await import(url)
|
|
71
|
+
} catch {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Last resort: the bare specifier, which works when the caller (the profile)
|
|
76
|
+
// can already resolve it.
|
|
77
|
+
try {
|
|
78
|
+
return await import(spec)
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'dsh-as-aistudio: the platform package ' +
|
|
82
|
+
JSON.stringify(spec) +
|
|
83
|
+
' could not be resolved from ' +
|
|
84
|
+
here +
|
|
85
|
+
'; it ships with the dsh installation, so this copy is being loaded outside a harness (' +
|
|
86
|
+
String((error && error.message) || error) +
|
|
87
|
+
')',
|
|
88
|
+
{ cause: error },
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolve one platform package, or fail with a message that names it.
|
|
95
|
+
*
|
|
96
|
+
* Eager on purpose: a component whose platform package is missing must fail its
|
|
97
|
+
* row loudly (the Loader records the reason and /status reports hostMounted:
|
|
98
|
+
* false), never mount half a feature and answer "unsupported" forever after.
|
|
99
|
+
*
|
|
100
|
+
* @param spec - the platform package name.
|
|
101
|
+
* @returns its module namespace.
|
|
102
|
+
*/
|
|
103
|
+
async function importPlatformOrThrow(spec) {
|
|
104
|
+
for (const base of bases()) {
|
|
105
|
+
try {
|
|
106
|
+
const url = createRequire(join(base, 'noop.js')).resolve(spec)
|
|
107
|
+
return await import(url)
|
|
108
|
+
} catch {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
return await import(spec)
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
'dsh-as-aistudio: the platform package ' +
|
|
117
|
+
JSON.stringify(spec) +
|
|
118
|
+
' could not be resolved from ' +
|
|
119
|
+
here +
|
|
120
|
+
'; it ships with the dsh installation, so this copy is being loaded outside a harness (' +
|
|
121
|
+
String((error && error.message) || error) +
|
|
122
|
+
')',
|
|
123
|
+
{ cause: error },
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `@deepseek-ai/dsh-tools`, resolved from the running harness. */
|
|
129
|
+
export const dshTools = await importPlatformOrThrow('@deepseek-ai/dsh-tools')
|
|
130
|
+
|
|
131
|
+
/** `@deepseek-ai/dsh-session`, resolved from the running harness. */
|
|
132
|
+
export const dshSession = await importPlatformOrThrow('@deepseek-ai/dsh-session')
|