@luv1211/dsh-pet 0.1.1-rc.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 -0
- package/README.i18n.yaml +6 -0
- package/README.md +35 -0
- package/README.zh.md +35 -0
- package/assets/deepseek-whale/pet.json +12 -0
- package/assets/deepseek-whale/spritesheet.webp +0 -0
- package/lib/index.js +1613 -0
- package/lib/invariant.js +19 -0
- package/lib/types/activity.d.ts +29 -0
- package/lib/types/activity.js +63 -0
- package/lib/types/catalog.d.ts +67 -0
- package/lib/types/catalog.js +258 -0
- package/lib/types/client.d.ts +13 -0
- package/lib/types/client.js +10 -0
- package/lib/types/host-image.d.ts +11 -0
- package/lib/types/host-image.js +48 -0
- package/lib/types/host-native.d.ts +17 -0
- package/lib/types/host-native.js +50 -0
- package/lib/types/index.d.ts +114 -0
- package/lib/types/index.js +631 -0
- package/lib/types/invariant.d.ts +9 -0
- package/lib/types/invariant.js +18 -0
- package/lib/types/path-opener.d.ts +50 -0
- package/lib/types/path-opener.js +161 -0
- package/lib/types/renderer.d.ts +42 -0
- package/lib/types/renderer.js +54 -0
- package/lib/types/runtime.d.ts +155 -0
- package/lib/types/runtime.js +314 -0
- package/lib/types/types.d.ts +147 -0
- package/lib/types/types.js +7 -0
- package/package.json +111 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/** Browser-safe pet constants, validation, presentation selection, and timing. */
|
|
2
|
+
import { DEFAULT_PET_ANIMATIONS, parsePetPackage } from '@luv1211/dsh-pet-compat';
|
|
3
|
+
import { imageDimensionsFromData } from 'image-dimensions';
|
|
4
|
+
/** Version of the durable pet preference document. */
|
|
5
|
+
export const PET_PREFERENCE_VERSION = 3;
|
|
6
|
+
/** Built-in identifier selected by a fresh preference document. */
|
|
7
|
+
export const DEFAULT_PET_ID = 'deepseek-whale';
|
|
8
|
+
/** Default logical CSS height of one compatible atlas cell. */
|
|
9
|
+
export const DEFAULT_PET_SIZE_PX = 112;
|
|
10
|
+
/** Minimum logical CSS height accepted by the pet preference validator. */
|
|
11
|
+
export const MIN_PET_SIZE_PX = 80;
|
|
12
|
+
/** Maximum logical CSS height accepted by the pet preference validator. */
|
|
13
|
+
export const MAX_PET_SIZE_PX = 224;
|
|
14
|
+
/** Compatible atlas geometry owned by the DSH renderer. */
|
|
15
|
+
export const PET_COMPAT_ATLAS = Object.freeze({
|
|
16
|
+
width: 1536,
|
|
17
|
+
height: 1872,
|
|
18
|
+
cellWidth: 192,
|
|
19
|
+
cellHeight: 208,
|
|
20
|
+
columns: 8,
|
|
21
|
+
rows: 9,
|
|
22
|
+
});
|
|
23
|
+
/** Stable validation error that callers can diagnose without parsing messages. */
|
|
24
|
+
export class PetValidationError extends TypeError {
|
|
25
|
+
/** Machine-readable validation category. */
|
|
26
|
+
code = 'invalid-pet-package';
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'PetValidationError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Numeric status precedence, where lower values are selected first. */
|
|
33
|
+
const ACTIVITY_PRIORITY = {
|
|
34
|
+
'needs-input': 0,
|
|
35
|
+
blocked: 1,
|
|
36
|
+
ready: 2,
|
|
37
|
+
running: 3,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Sort activity records by user-action urgency, then newest state transition,
|
|
41
|
+
* then their stable opaque session ids.
|
|
42
|
+
* @param left - the first activity record.
|
|
43
|
+
* @param right - the second activity record.
|
|
44
|
+
* @returns a standard ascending sort comparison result.
|
|
45
|
+
*/
|
|
46
|
+
export function comparePetActivities(left, right) {
|
|
47
|
+
const priority = ACTIVITY_PRIORITY[left.status] - ACTIVITY_PRIORITY[right.status];
|
|
48
|
+
if (priority !== 0)
|
|
49
|
+
return priority;
|
|
50
|
+
if (left.since !== right.since)
|
|
51
|
+
return right.since - left.since;
|
|
52
|
+
return left.sessionId < right.sessionId ? -1 : left.sessionId > right.sessionId ? 1 : 0;
|
|
53
|
+
}
|
|
54
|
+
/** Resolve a missing preference or validate a current preference document.
|
|
55
|
+
* @param value - decoded preference value from the settings provider.
|
|
56
|
+
* @returns the validated v3 preference.
|
|
57
|
+
*/
|
|
58
|
+
export function resolvePetPreference(value) {
|
|
59
|
+
if (value === undefined || value === null)
|
|
60
|
+
return defaultPetPreference();
|
|
61
|
+
if (!isRecord(value) || typeof value.version !== 'number')
|
|
62
|
+
throw new TypeError('pet preference must be an object with a numeric version');
|
|
63
|
+
if (value.version !== PET_PREFERENCE_VERSION) {
|
|
64
|
+
throw new TypeError(`pet preference version ${String(value.version)} is unsupported (expected ${PET_PREFERENCE_VERSION})`);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
version: PET_PREFERENCE_VERSION,
|
|
68
|
+
selectedPetId: requirePetId(value.selectedPetId),
|
|
69
|
+
awake: requireBoolean(value.awake, 'awake'),
|
|
70
|
+
sizePx: validatePetSize(value.sizePx),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Return the fresh v3 preference defaults.
|
|
74
|
+
* @returns a new v3 preference document.
|
|
75
|
+
*/
|
|
76
|
+
export function defaultPetPreference() {
|
|
77
|
+
return { version: PET_PREFERENCE_VERSION, selectedPetId: DEFAULT_PET_ID, awake: true, sizePx: DEFAULT_PET_SIZE_PX };
|
|
78
|
+
}
|
|
79
|
+
/** Validate one logical CSS height.
|
|
80
|
+
* @param sizePx - candidate logical CSS height.
|
|
81
|
+
* @returns the validated height.
|
|
82
|
+
*/
|
|
83
|
+
export function validatePetSize(sizePx) {
|
|
84
|
+
if (typeof sizePx !== 'number' || !Number.isSafeInteger(sizePx) || sizePx < MIN_PET_SIZE_PX || sizePx > MAX_PET_SIZE_PX) {
|
|
85
|
+
throw new TypeError(`pet preference sizePx must be between ${MIN_PET_SIZE_PX} and ${MAX_PET_SIZE_PX}`);
|
|
86
|
+
}
|
|
87
|
+
return sizePx;
|
|
88
|
+
}
|
|
89
|
+
/** Return true only after pointer movement exceeds the shared four-pixel drag threshold.
|
|
90
|
+
* @param deltaX - horizontal pointer displacement.
|
|
91
|
+
* @param deltaY - vertical pointer displacement.
|
|
92
|
+
* @param threshold - minimum Euclidean displacement.
|
|
93
|
+
* @returns whether the displacement is a drag.
|
|
94
|
+
*/
|
|
95
|
+
export function isDragMovement(deltaX, deltaY, threshold = 4) {
|
|
96
|
+
return Number.isFinite(deltaX) && Number.isFinite(deltaY) && Number.isFinite(threshold)
|
|
97
|
+
&& threshold >= 0 && Math.hypot(deltaX, deltaY) > threshold;
|
|
98
|
+
}
|
|
99
|
+
/** Derive the logical CSS width from one validated atlas-cell height.
|
|
100
|
+
* @param sizePx - validated logical CSS height.
|
|
101
|
+
* @returns the corresponding logical CSS width.
|
|
102
|
+
*/
|
|
103
|
+
export function petWidthForSize(sizePx) {
|
|
104
|
+
return Math.round(validatePetSize(sizePx) * PET_COMPAT_ATLAS.cellWidth / PET_COMPAT_ATLAS.cellHeight);
|
|
105
|
+
}
|
|
106
|
+
/** Pick one of sixteen clockwise look-direction cells from a relative target.
|
|
107
|
+
* @param target - relative pointer target, or `undefined` for neutral direction.
|
|
108
|
+
* @returns a direction index from zero through fifteen.
|
|
109
|
+
*/
|
|
110
|
+
export function selectLookDirection(target) {
|
|
111
|
+
if (target === undefined || !Number.isFinite(target.x) || !Number.isFinite(target.y))
|
|
112
|
+
return 0;
|
|
113
|
+
if (Math.abs(target.x) <= 1 && Math.abs(target.y) <= 1)
|
|
114
|
+
return 0;
|
|
115
|
+
const angle = Math.atan2(target.x, -target.y);
|
|
116
|
+
const normalized = angle < 0 ? angle + Math.PI * 2 : angle;
|
|
117
|
+
return Math.floor((normalized + Math.PI / 16) / (Math.PI / 8)) % 16;
|
|
118
|
+
}
|
|
119
|
+
/** Select the state and first frame used to render one presentation update.
|
|
120
|
+
* @param input - current host, pointer, motion, and wake state.
|
|
121
|
+
* @returns the renderer-independent presentation selection.
|
|
122
|
+
*/
|
|
123
|
+
export function selectPetPresentation(input) {
|
|
124
|
+
const lookDirection = selectLookDirection(input.lookTarget);
|
|
125
|
+
if (!input.awake)
|
|
126
|
+
return { state: 'tucked', row: 0, frame: 0, lookDirection, lookDirectionActive: false, animate: false };
|
|
127
|
+
let state = statusToAnimationState(input.status);
|
|
128
|
+
if (input.hover)
|
|
129
|
+
state = 'jumping';
|
|
130
|
+
else if (input.status === 'running' && input.dragDirection === 'left')
|
|
131
|
+
state = 'running-left';
|
|
132
|
+
else if (input.status === 'running' && input.dragDirection === 'right')
|
|
133
|
+
state = 'running-right';
|
|
134
|
+
const spriteIndex = DEFAULT_PET_ANIMATIONS[state]?.frames[0]?.spriteIndex ?? 0;
|
|
135
|
+
return {
|
|
136
|
+
state,
|
|
137
|
+
row: Math.floor(spriteIndex / PET_COMPAT_ATLAS.columns),
|
|
138
|
+
frame: spriteIndex % PET_COMPAT_ATLAS.columns,
|
|
139
|
+
lookDirection,
|
|
140
|
+
lookDirectionActive: false,
|
|
141
|
+
animate: !input.reducedMotion,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Validate a compatible manifest and its WebP dimensions without Node or filesystem APIs.
|
|
145
|
+
* @param manifestBytes - UTF-8 pet.json bytes.
|
|
146
|
+
* @param spritesheetBytes - WebP atlas bytes.
|
|
147
|
+
* @param options - catalog origin, optional asset URL, and byte limits.
|
|
148
|
+
* @returns a sanitized descriptor suitable for client transport.
|
|
149
|
+
*/
|
|
150
|
+
export function validatePetPackage(manifestBytes, spritesheetBytes, options) {
|
|
151
|
+
return validatePetPackageFiles(manifestBytes, spritesheetBytes, options).descriptor;
|
|
152
|
+
}
|
|
153
|
+
/** Validate package bytes and retain the manifest-relative asset location for host storage.
|
|
154
|
+
* @param manifestBytes - UTF-8 pet.json bytes.
|
|
155
|
+
* @param spritesheetBytes - WebP atlas bytes.
|
|
156
|
+
* @param options - catalog origin, optional asset URL, and byte limits.
|
|
157
|
+
* @returns sanitized client metadata and the validated relative spritesheet path.
|
|
158
|
+
*/
|
|
159
|
+
export function validatePetPackageFiles(manifestBytes, spritesheetBytes, options) {
|
|
160
|
+
const maxManifestBytes = options.maxManifestBytes ?? 16 * 1024;
|
|
161
|
+
const maxSpriteBytes = options.maxSpriteBytes ?? 16 * 1024 * 1024;
|
|
162
|
+
if (!Number.isSafeInteger(maxManifestBytes) || maxManifestBytes <= 0)
|
|
163
|
+
throw new PetValidationError('manifest byte limit is invalid');
|
|
164
|
+
if (!Number.isSafeInteger(maxSpriteBytes) || maxSpriteBytes <= 0)
|
|
165
|
+
throw new PetValidationError('spritesheet byte limit is invalid');
|
|
166
|
+
if (manifestBytes.byteLength > maxManifestBytes)
|
|
167
|
+
throw new PetValidationError('pet manifest exceeds the configured byte limit');
|
|
168
|
+
if (spritesheetBytes.byteLength === 0 || spritesheetBytes.byteLength > maxSpriteBytes)
|
|
169
|
+
throw new PetValidationError('pet spritesheet exceeds the configured byte limit');
|
|
170
|
+
parseManifest(manifestBytes, { width: PET_COMPAT_ATLAS.width, height: PET_COMPAT_ATLAS.height });
|
|
171
|
+
const dimensions = webpDimensions(spritesheetBytes);
|
|
172
|
+
const pet = parseManifest(manifestBytes, dimensions);
|
|
173
|
+
const assetUrl = options.assetUrl ?? '';
|
|
174
|
+
if (assetUrl !== '' && !isOriginRelativePathname(assetUrl))
|
|
175
|
+
throw new PetValidationError('pet assetUrl must be an origin-relative pathname');
|
|
176
|
+
const descriptor = Object.freeze({
|
|
177
|
+
id: pet.id,
|
|
178
|
+
source: options.source,
|
|
179
|
+
displayName: pet.displayName,
|
|
180
|
+
...(pet.description === '' ? {} : { description: pet.description }),
|
|
181
|
+
frame: pet.frame,
|
|
182
|
+
animations: pet.animations,
|
|
183
|
+
assetUrl,
|
|
184
|
+
});
|
|
185
|
+
return Object.freeze({ descriptor, spritesheetPath: pet.spritesheetPath });
|
|
186
|
+
}
|
|
187
|
+
/** Resolve the safe relative spritesheet location before reading the image file.
|
|
188
|
+
* @param manifestBytes - bounded UTF-8 pet.json bytes.
|
|
189
|
+
* @returns the manifest-relative spritesheet path.
|
|
190
|
+
*/
|
|
191
|
+
export function petSpritesheetPath(manifestBytes) {
|
|
192
|
+
return parseManifest(manifestBytes, {
|
|
193
|
+
width: PET_COMPAT_ATLAS.width,
|
|
194
|
+
height: PET_COMPAT_ATLAS.height,
|
|
195
|
+
}).spritesheetPath;
|
|
196
|
+
}
|
|
197
|
+
/** Convert a host activity record into the pet's display status.
|
|
198
|
+
* @param record - detached host activity record.
|
|
199
|
+
* @returns the display status, or `undefined` when the record is idle.
|
|
200
|
+
*/
|
|
201
|
+
export function petStatusForHostActivity(record) {
|
|
202
|
+
if (record.pendingInteraction !== undefined)
|
|
203
|
+
return 'needs-input';
|
|
204
|
+
if (record.status === 'blocked')
|
|
205
|
+
return 'blocked';
|
|
206
|
+
if (record.completed)
|
|
207
|
+
return 'ready';
|
|
208
|
+
if (record.status === 'running')
|
|
209
|
+
return 'running';
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
function statusToAnimationState(status) {
|
|
213
|
+
switch (status) {
|
|
214
|
+
case 'needs-input': return 'waiting';
|
|
215
|
+
case 'blocked': return 'failed';
|
|
216
|
+
case 'ready': return 'review';
|
|
217
|
+
case 'running': return 'running';
|
|
218
|
+
default: return 'idle';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function requirePetId(value) {
|
|
222
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > 64)
|
|
223
|
+
throw new TypeError('pet preference selectedPetId must be a non-empty string');
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
function requireBoolean(value, name) {
|
|
227
|
+
if (typeof value !== 'boolean')
|
|
228
|
+
throw new TypeError(`pet preference ${name} must be boolean`);
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
function isRecord(value) {
|
|
232
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
233
|
+
}
|
|
234
|
+
function webpDimensions(bytes) {
|
|
235
|
+
assertCompleteWebp(bytes);
|
|
236
|
+
const dimensions = imageDimensionsFromData(bytes);
|
|
237
|
+
if (dimensions?.type !== 'webp')
|
|
238
|
+
throw new PetValidationError('pet spritesheet must be a valid WebP image');
|
|
239
|
+
return { width: dimensions.width, height: dimensions.height };
|
|
240
|
+
}
|
|
241
|
+
function assertCompleteWebp(bytes) {
|
|
242
|
+
if (bytes.length < 20 || ascii(bytes, 0, 4) !== 'RIFF' || ascii(bytes, 8, 4) !== 'WEBP') {
|
|
243
|
+
throw new PetValidationError('pet spritesheet must be a valid WebP image');
|
|
244
|
+
}
|
|
245
|
+
if (readUint32(bytes, 4) + 8 !== bytes.length)
|
|
246
|
+
throw new PetValidationError('pet spritesheet must be a valid WebP image');
|
|
247
|
+
let offset = 12;
|
|
248
|
+
let hasImagePayload = false;
|
|
249
|
+
while (offset + 8 <= bytes.length) {
|
|
250
|
+
const chunk = ascii(bytes, offset, 4);
|
|
251
|
+
const size = readUint32(bytes, offset + 4);
|
|
252
|
+
const data = offset + 8;
|
|
253
|
+
if (data + size > bytes.length)
|
|
254
|
+
throw new PetValidationError('pet spritesheet must be a valid WebP image');
|
|
255
|
+
if (chunk === 'VP8 ' && size >= 10 && (readByte(bytes, data) & 1) === 0
|
|
256
|
+
&& bytes[data + 3] === 0x9d && bytes[data + 4] === 0x01 && bytes[data + 5] === 0x2a)
|
|
257
|
+
hasImagePayload = true;
|
|
258
|
+
if (chunk === 'VP8L' && size >= 5 && bytes[data] === 0x2f)
|
|
259
|
+
hasImagePayload = true;
|
|
260
|
+
offset = data + size + (size % 2);
|
|
261
|
+
}
|
|
262
|
+
if (offset !== bytes.length || !hasImagePayload)
|
|
263
|
+
throw new PetValidationError('pet spritesheet must be a valid WebP image');
|
|
264
|
+
}
|
|
265
|
+
function parseManifest(manifestBytes, dimensions) {
|
|
266
|
+
let value;
|
|
267
|
+
try {
|
|
268
|
+
value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(manifestBytes));
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
throw new PetValidationError('pet manifest must be valid UTF-8 JSON');
|
|
272
|
+
}
|
|
273
|
+
if (!isRecord(value) || Array.isArray(value))
|
|
274
|
+
throw new PetValidationError('pet manifest must be a JSON object');
|
|
275
|
+
const allowed = new Set(['id', 'displayName', 'description', 'spritesheetPath', 'frame', 'animations', 'kind', 'spriteVersionNumber']);
|
|
276
|
+
for (const key of Object.keys(value))
|
|
277
|
+
if (!allowed.has(key))
|
|
278
|
+
throw new PetValidationError(`pet manifest contains unsupported field ${key}`);
|
|
279
|
+
const id = value.id;
|
|
280
|
+
if (typeof id !== 'string' || !/^[\p{L}][\p{L}\p{N}._-]{0,63}$/u.test(id))
|
|
281
|
+
throw new PetValidationError('pet manifest id is invalid');
|
|
282
|
+
const displayName = value.displayName;
|
|
283
|
+
if (typeof displayName !== 'string' || displayName.length === 0 || displayName.length > 80)
|
|
284
|
+
throw new PetValidationError('pet manifest displayName is invalid');
|
|
285
|
+
const description = value.description;
|
|
286
|
+
if (description !== undefined && (typeof description !== 'string' || description.length > 500))
|
|
287
|
+
throw new PetValidationError('pet manifest description is invalid');
|
|
288
|
+
const parsed = parsePetPackage({ ...value, spritesheetDimensions: dimensions });
|
|
289
|
+
if (!parsed.accepted)
|
|
290
|
+
throw new PetValidationError(`pet package is incompatible: ${parsed.reason}`);
|
|
291
|
+
return parsed.pet;
|
|
292
|
+
}
|
|
293
|
+
function ascii(bytes, offset, length) {
|
|
294
|
+
return String.fromCharCode(...bytes.subarray(offset, offset + length));
|
|
295
|
+
}
|
|
296
|
+
function readUint32(bytes, offset) {
|
|
297
|
+
return readByte(bytes, offset)
|
|
298
|
+
+ (readByte(bytes, offset + 1) << 8)
|
|
299
|
+
+ (readByte(bytes, offset + 2) << 16)
|
|
300
|
+
+ (readByte(bytes, offset + 3) * 0x1000000);
|
|
301
|
+
}
|
|
302
|
+
function readByte(bytes, offset) {
|
|
303
|
+
const value = bytes[offset];
|
|
304
|
+
if (value === undefined)
|
|
305
|
+
throw new PetValidationError('pet spritesheet has a truncated chunk');
|
|
306
|
+
return value;
|
|
307
|
+
}
|
|
308
|
+
function isOriginRelativePathname(value) {
|
|
309
|
+
if (!value.startsWith('/') || value.startsWith('//'))
|
|
310
|
+
return false;
|
|
311
|
+
const parsed = new URL(value, 'http://dsh.local');
|
|
312
|
+
return parsed.origin === 'http://dsh.local' && parsed.pathname === value && parsed.search === '' && parsed.hash === '';
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-safe types for durable pet preferences and globally aggregated
|
|
3
|
+
* session activity.
|
|
4
|
+
* @module @luv1211/dsh-pet/types
|
|
5
|
+
*/
|
|
6
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
7
|
+
import type { PetAnimation, PetFrameGeometry } from '@luv1211/dsh-pet-compat/types';
|
|
8
|
+
/** The only durable preference document accepted by the compatible pet domain. */
|
|
9
|
+
export interface PetPreference {
|
|
10
|
+
/** Schema version of the preference document. */
|
|
11
|
+
readonly version: 3;
|
|
12
|
+
/** Identifier of the selected built-in or imported pet package. */
|
|
13
|
+
readonly selectedPetId: string;
|
|
14
|
+
/** Whether the selected pet is awake and rendered by companion clients. */
|
|
15
|
+
readonly awake: boolean;
|
|
16
|
+
/** Logical CSS height of one atlas cell. */
|
|
17
|
+
readonly sizePx: number;
|
|
18
|
+
}
|
|
19
|
+
/** A validated compatible package descriptor safe to expose to clients. */
|
|
20
|
+
export interface PetDescriptor {
|
|
21
|
+
/** Stable package identifier. */
|
|
22
|
+
readonly id: string;
|
|
23
|
+
/** Whether the catalog loaded the package from its embedded assets or the user root. */
|
|
24
|
+
readonly source: 'builtin' | 'user';
|
|
25
|
+
/** Human-readable package name. */
|
|
26
|
+
readonly displayName: string;
|
|
27
|
+
/** Optional bounded package description. */
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
/** Validated sprite-cell geometry. */
|
|
30
|
+
readonly frame: PetFrameGeometry;
|
|
31
|
+
/** Validated named animation tracks. */
|
|
32
|
+
readonly animations: Readonly<Record<string, PetAnimation>>;
|
|
33
|
+
/** Origin-relative URL for the validated spritesheet. */
|
|
34
|
+
readonly assetUrl: string;
|
|
35
|
+
}
|
|
36
|
+
/** Detached catalog read model. */
|
|
37
|
+
export interface PetCatalog {
|
|
38
|
+
/** Immutable built-in and user package descriptors in deterministic order. */
|
|
39
|
+
readonly pets: readonly PetDescriptor[];
|
|
40
|
+
}
|
|
41
|
+
/** Native operations advertised by the current host composition. */
|
|
42
|
+
export interface PetHostCapabilities {
|
|
43
|
+
/** True when a host-native package picker is available. */
|
|
44
|
+
readonly canImport: boolean;
|
|
45
|
+
/** True when the host can open the DSH pet directory. */
|
|
46
|
+
readonly canOpenFolder: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Pending interaction kinds the host activity projection can report. */
|
|
49
|
+
export type PetPendingInteraction = 'approval' | 'plan-review' | 'question';
|
|
50
|
+
/** Host-owned session activity record consumed by the pet adapter. */
|
|
51
|
+
export interface PetHostActivityRecord {
|
|
52
|
+
/** Opaque session identifier. */
|
|
53
|
+
readonly sessionId: SessionId;
|
|
54
|
+
/** Stable display fallback. */
|
|
55
|
+
readonly title: string;
|
|
56
|
+
/** Host's current coarse session condition. */
|
|
57
|
+
readonly status: 'running' | 'blocked' | 'idle';
|
|
58
|
+
/** Epoch ms when the host condition changed. */
|
|
59
|
+
readonly since: number;
|
|
60
|
+
/** Pending interaction, when user input currently blocks progress. */
|
|
61
|
+
readonly pendingInteraction?: PetPendingInteraction;
|
|
62
|
+
/** Whether the host still exposes a completion notification for this session. */
|
|
63
|
+
readonly completed: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Host activity projection seam used by PetService. */
|
|
66
|
+
export interface PetActivitySource {
|
|
67
|
+
/** Read a detached current host projection. */
|
|
68
|
+
getSnapshot(): readonly PetHostActivityRecord[];
|
|
69
|
+
/** Subscribe to detached projection replacements and return its disposer. */
|
|
70
|
+
subscribe(listener: (records: readonly PetHostActivityRecord[]) => void): () => void;
|
|
71
|
+
}
|
|
72
|
+
/** Bytes selected by a host-native package picker; the client never supplies a path. */
|
|
73
|
+
export interface PetPackageBytes {
|
|
74
|
+
/** UTF-8 compatible pet.json bytes. */
|
|
75
|
+
readonly manifestBytes: Uint8Array;
|
|
76
|
+
/** Validated by PetService before publication. */
|
|
77
|
+
readonly spritesheetBytes: Uint8Array;
|
|
78
|
+
}
|
|
79
|
+
/** Native-only operations supplied by a local host composition. */
|
|
80
|
+
export interface PetNativeActions {
|
|
81
|
+
/** Open the native package chooser and return bytes, or null on cancellation. */
|
|
82
|
+
pickPetPackage(): Promise<PetPackageBytes | null>;
|
|
83
|
+
/** Open one service-owned directory; hosts never accept a client-supplied path here. */
|
|
84
|
+
openPetFolder(path: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
/** Result of a Remote import request with no native host capability. */
|
|
87
|
+
export type PetImportResult = {
|
|
88
|
+
readonly outcome: 'published';
|
|
89
|
+
readonly pet: PetDescriptor;
|
|
90
|
+
} | {
|
|
91
|
+
readonly outcome: 'cancelled' | 'host-unavailable';
|
|
92
|
+
};
|
|
93
|
+
/** Result of a Remote request to open the DSH pet directory. */
|
|
94
|
+
export type PetFolderResult = {
|
|
95
|
+
readonly outcome: 'opened';
|
|
96
|
+
} | {
|
|
97
|
+
readonly outcome: 'host-unavailable';
|
|
98
|
+
};
|
|
99
|
+
declare module '@deepseek-ai/cordis' {
|
|
100
|
+
interface Context {
|
|
101
|
+
/** Optional host-owned activity projection consumed by the pet service. */
|
|
102
|
+
petActivity?: PetActivitySource;
|
|
103
|
+
/** Optional native-only pet actions; browser compositions leave it absent. */
|
|
104
|
+
petNative?: PetNativeActions;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** The user-facing urgency states of one live session. */
|
|
108
|
+
export type PetActivityStatus = 'running' | 'needs-input' | 'ready' | 'blocked';
|
|
109
|
+
/** One current activity record aggregated from a live session. */
|
|
110
|
+
export interface PetSessionActivity {
|
|
111
|
+
/** Opaque identifier of the represented session. */
|
|
112
|
+
readonly sessionId: SessionId;
|
|
113
|
+
/** Stable display fallback for clients that have not loaded a session title. */
|
|
114
|
+
readonly title: string;
|
|
115
|
+
/** Current user-action urgency. */
|
|
116
|
+
readonly status: PetActivityStatus;
|
|
117
|
+
/** Epoch ms when the activity entered its current status. */
|
|
118
|
+
readonly since: number;
|
|
119
|
+
}
|
|
120
|
+
/** The remote pet read model: durable preference plus ordered live activity. */
|
|
121
|
+
export interface PetSnapshot {
|
|
122
|
+
/** Persisted user preference. */
|
|
123
|
+
readonly preference: PetPreference;
|
|
124
|
+
/** Validated package catalog. */
|
|
125
|
+
readonly catalog: PetCatalog;
|
|
126
|
+
/** Host-local absolute path of the user package root, for display beside the managed-directory actions. */
|
|
127
|
+
readonly petRoot: string;
|
|
128
|
+
/** Capability flags used to gate native-only controls. */
|
|
129
|
+
readonly capabilities: PetHostCapabilities;
|
|
130
|
+
/** All live-session records in deterministic selection order. */
|
|
131
|
+
readonly activities: readonly PetSessionActivity[];
|
|
132
|
+
/** Highest-priority activity when one exists. */
|
|
133
|
+
readonly selectedActivity?: PetSessionActivity;
|
|
134
|
+
}
|
|
135
|
+
declare module '@deepseek-ai/cordis' {
|
|
136
|
+
interface Events {
|
|
137
|
+
/**
|
|
138
|
+
* The pet service published a fresh durable preference or live activity
|
|
139
|
+
* snapshot. Preference mutations are emitted after the settings provider
|
|
140
|
+
* persists the namespace.
|
|
141
|
+
* @param snapshot - detached fresh preference and ordered activity records.
|
|
142
|
+
* @mode emit
|
|
143
|
+
*/
|
|
144
|
+
'pet/update'(snapshot: PetSnapshot): void;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@luv1211/dsh-pet",
|
|
3
|
+
"description": "Global settings-backed desktop pet companion with a Remote activity surface",
|
|
4
|
+
"version": "0.1.1-rc.2",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/pet/pet"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./lib/types/types.d.ts",
|
|
27
|
+
"default": "./lib/types/types.js"
|
|
28
|
+
},
|
|
29
|
+
"./client": {
|
|
30
|
+
"types": "./lib/types/client.d.ts",
|
|
31
|
+
"default": "./lib/types/client.js"
|
|
32
|
+
},
|
|
33
|
+
"./runtime": {
|
|
34
|
+
"types": "./lib/types/runtime.d.ts",
|
|
35
|
+
"default": "./lib/types/runtime.js"
|
|
36
|
+
},
|
|
37
|
+
"./renderer": {
|
|
38
|
+
"types": "./lib/types/renderer.d.ts",
|
|
39
|
+
"default": "./lib/types/renderer.js"
|
|
40
|
+
},
|
|
41
|
+
"./typert": {
|
|
42
|
+
"types": "./lib/typert.host.d.ts",
|
|
43
|
+
"default": "./lib/typert.host.js"
|
|
44
|
+
},
|
|
45
|
+
"./remote": {
|
|
46
|
+
"types": "./lib/typert.remote-client.d.ts",
|
|
47
|
+
"default": "./lib/typert.remote-client.js"
|
|
48
|
+
},
|
|
49
|
+
"./src/*": "./src/*",
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"lib/index.js",
|
|
54
|
+
"lib/invariant.js",
|
|
55
|
+
"assets",
|
|
56
|
+
"lib/types/**/*.js",
|
|
57
|
+
"lib/types/**/*.d.ts",
|
|
58
|
+
"lib/typert.host.js",
|
|
59
|
+
"lib/typert.host.d.ts",
|
|
60
|
+
"lib/typert.remote-client.js",
|
|
61
|
+
"lib/typert.remote-client.d.ts"
|
|
62
|
+
],
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.4",
|
|
66
|
+
"@deepseek-ai/dsh-goal": "^0.1.1-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.1-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-host-directory-picker": "^0.1.1-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-native-command": "^0.1.1-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
|
|
78
|
+
"@luv1211/dsh-desktop-companion": "^0.1.1-rc.2",
|
|
79
|
+
"@luv1211/dsh-pet-compat": "^0.1.1-rc.2"
|
|
80
|
+
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@deepseek-ai/schemastery": "^3.18.1-rc.4",
|
|
83
|
+
"image-dimensions": "^2.5.1",
|
|
84
|
+
"sharp": "0.35.3",
|
|
85
|
+
"zod": "^4.4.3"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.4",
|
|
89
|
+
"@deepseek-ai/cordis-plugin-include": "^1.0.6-rc.4",
|
|
90
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.2-rc.4",
|
|
91
|
+
"@deepseek-ai/dsh-goal": "^0.1.1-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.1-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-host-directory-picker": "^0.1.1-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
|
|
95
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
96
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
97
|
+
"@deepseek-ai/dsh-loader-smoke": "^0.1.1-rc.2",
|
|
98
|
+
"@deepseek-ai/dsh-native-command": "^0.1.1-rc.2",
|
|
99
|
+
"@deepseek-ai/dsh-session": "^0.1.1-rc.2",
|
|
100
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.1-rc.2",
|
|
101
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
102
|
+
"@deepseek-ai/dsh-settings-file": "^0.1.1-rc.2",
|
|
103
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
|
|
104
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
|
|
105
|
+
"@luv1211/dsh-desktop-companion": "^0.1.1-rc.2",
|
|
106
|
+
"@luv1211/dsh-pet-compat": "^0.1.1-rc.2"
|
|
107
|
+
},
|
|
108
|
+
"scripts": {
|
|
109
|
+
"bundle": "tsdown"
|
|
110
|
+
}
|
|
111
|
+
}
|