@khorsheed/dsh-file-preview 0.2.0 → 0.3.1
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 +19 -0
- package/README.en.md +9 -6
- package/README.i18n.yaml +2 -2
- package/README.md +9 -6
- package/lib/index.js +196 -80
- package/lib/typert.host.js +291 -69
- package/lib/typert.remote-client.d.ts +6 -1
- package/lib/typert.remote-client.js +137 -29
- package/lib/types/fold.d.ts +7 -6
- package/lib/types/fold.js +30 -37
- package/lib/types/index.d.ts +29 -3
- package/lib/types/index.js +76 -59
- package/lib/types/open-external.d.ts +37 -0
- package/lib/types/open-external.js +61 -0
- package/lib/types/types.d.ts +11 -1
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +7 -17
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/lib/typert.remote-client.d.ts.map +0 -1
- package/lib/types/bash-writes.d.ts.map +0 -1
- package/lib/types/fold.d.ts.map +0 -1
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/invariant.d.ts.map +0 -1
- package/lib/types/reveal.d.ts.map +0 -1
- package/lib/types/types.d.ts.map +0 -1
- package/skills/3d-artifact/SKILL.md +0 -52
package/lib/types/index.js
CHANGED
|
@@ -35,64 +35,12 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
35
35
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
36
36
|
done = true;
|
|
37
37
|
};
|
|
38
|
-
import { readFileSync } from 'node:fs';
|
|
39
|
-
import { dirname, join } from 'node:path';
|
|
40
|
-
import { fileURLToPath } from 'node:url';
|
|
41
38
|
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
42
39
|
import z from '@deepseek-ai/schemastery';
|
|
43
40
|
import { BashWriteCollector } from './bash-writes.js';
|
|
44
41
|
import { foldFilePreview, foldFilePreviewByTurn } from './fold.js';
|
|
42
|
+
import { openExternalNative, macAppName } from './open-external.js';
|
|
45
43
|
import { revealNativePath } from './reveal.js';
|
|
46
|
-
/**
|
|
47
|
-
* Register the 3d-artifact skill — the generation-side contract for
|
|
48
|
-
* sandbox-runnable interactive 3D / digital-twin single-file HTML (self-
|
|
49
|
-
* contained, zero runtime network, GLB-inline zero-fetch models). Pull-based
|
|
50
|
-
* discovery: an agent whose task involves generating such an HTML page finds
|
|
51
|
-
* the contract through the skill catalog — no per-session push notice.
|
|
52
|
-
* Optional: compositions without the skill capability skip the registration.
|
|
53
|
-
* A missing/malformed shipped SKILL.md degrades to a warning — a discovery
|
|
54
|
-
* aid must never take a boot down; the pack-smoke test owns the file's
|
|
55
|
-
* presence in the tarball.
|
|
56
|
-
* @param ctx - plugin context.
|
|
57
|
-
*/
|
|
58
|
-
function registerArtifactSkill(ctx) {
|
|
59
|
-
const skills = ctx.get('skills');
|
|
60
|
-
if (skills === undefined) {
|
|
61
|
-
// Not a crash: file preview degrades without the skill (and a minimal
|
|
62
|
-
// composition may legitimately lack the capability). The line exists so a
|
|
63
|
-
// host-API migration that drops or renames the skills service shows up in
|
|
64
|
-
// boot logs instead of failing silently — same diagnostic as ankh-guard's
|
|
65
|
-
// restart-skill registration; grep "skill .* not registered".
|
|
66
|
-
ctx.logger.warn('file-preview: skills capability absent — the 3d-artifact skill is not registered');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const skillDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'skills', '3d-artifact');
|
|
71
|
-
const skillFile = join(skillDir, 'SKILL.md');
|
|
72
|
-
const raw = readFileSync(skillFile, 'utf8');
|
|
73
|
-
const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(raw);
|
|
74
|
-
const name = /^name: (.+)$/m.exec(match?.[1] ?? '')?.[1]?.trim();
|
|
75
|
-
const description = /^description: (.+)$/m.exec(match?.[1] ?? '')?.[1]?.trim();
|
|
76
|
-
const content = match?.[2];
|
|
77
|
-
if (match === null || name === undefined || description === undefined || content === undefined) {
|
|
78
|
-
ctx.logger.warn('file-preview: shipped SKILL.md is malformed — the 3d-artifact skill is not registered');
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
ctx.effect(() => skills.register({
|
|
82
|
-
name,
|
|
83
|
-
description,
|
|
84
|
-
content,
|
|
85
|
-
source: 'runtime',
|
|
86
|
-
provider: 'file-preview',
|
|
87
|
-
// Content-only (only SKILL.md in the bundle): no `resourceBase`, so the
|
|
88
|
-
// catalog renders a virtual single-SKILL.md node. Add `resourceBase`
|
|
89
|
-
// only when the bundle later gains scripts/assets beside SKILL.md.
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
ctx.logger.warn(`file-preview: shipped SKILL.md unreadable (${String(error)}) — the 3d-artifact skill is not registered`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
44
|
/** Extensions treated as binary without reading (their text decode is meaningless). */
|
|
97
45
|
const BINARY_EXTENSIONS = new Set([
|
|
98
46
|
'.pdf', '.zip', '.gz', '.tar', '.7z', '.zstd', '.br',
|
|
@@ -121,6 +69,10 @@ const IMAGE_MIME = {
|
|
|
121
69
|
function hasNulByte(content) {
|
|
122
70
|
return content.slice(0, 4096).includes('\u0000');
|
|
123
71
|
}
|
|
72
|
+
/** Shared non-fatal UTF-8 decoder for the bounded byte-window read: invalid
|
|
73
|
+
* sequences become U+FFFD (a cut multi-byte character at the window edge
|
|
74
|
+
* included) instead of failing the read. */
|
|
75
|
+
const utf8Decoder = new TextDecoder();
|
|
124
76
|
/** Detect a binary file from its display path extension. */
|
|
125
77
|
function isBinaryPath(path) {
|
|
126
78
|
const dot = path.lastIndexOf('.');
|
|
@@ -165,21 +117,27 @@ function readErrorMessage(error) {
|
|
|
165
117
|
let FilePreviewService = (() => {
|
|
166
118
|
let _classSuper = TypertRemoteService;
|
|
167
119
|
let _instanceExtraInitializers = [];
|
|
120
|
+
let _capabilities_decorators;
|
|
168
121
|
let _list_decorators;
|
|
169
122
|
let _turnFiles_decorators;
|
|
170
123
|
let _read_decorators;
|
|
171
124
|
let _reveal_decorators;
|
|
125
|
+
let _openExternal_decorators;
|
|
172
126
|
return class FilePreviewService extends _classSuper {
|
|
173
127
|
static {
|
|
174
128
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
129
|
+
_capabilities_decorators = [Remote('capabilities')];
|
|
175
130
|
_list_decorators = [Remote('list')];
|
|
176
131
|
_turnFiles_decorators = [Remote('turnFiles')];
|
|
177
132
|
_read_decorators = [Remote('read')];
|
|
178
133
|
_reveal_decorators = [Remote('reveal')];
|
|
134
|
+
_openExternal_decorators = [Remote('openExternal')];
|
|
135
|
+
__esDecorate(this, null, _capabilities_decorators, { kind: "method", name: "capabilities", static: false, private: false, access: { has: obj => "capabilities" in obj, get: obj => obj.capabilities }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
179
136
|
__esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
180
137
|
__esDecorate(this, null, _turnFiles_decorators, { kind: "method", name: "turnFiles", static: false, private: false, access: { has: obj => "turnFiles" in obj, get: obj => obj.turnFiles }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
181
138
|
__esDecorate(this, null, _read_decorators, { kind: "method", name: "read", static: false, private: false, access: { has: obj => "read" in obj, get: obj => obj.read }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
182
139
|
__esDecorate(this, null, _reveal_decorators, { kind: "method", name: "reveal", static: false, private: false, access: { has: obj => "reveal" in obj, get: obj => obj.reveal }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
140
|
+
__esDecorate(this, null, _openExternal_decorators, { kind: "method", name: "openExternal", static: false, private: false, access: { has: obj => "openExternal" in obj, get: obj => obj.openExternal }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
183
141
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
184
142
|
}
|
|
185
143
|
static inject = ['fs'];
|
|
@@ -199,6 +157,8 @@ let FilePreviewService = (() => {
|
|
|
199
157
|
agents;
|
|
200
158
|
/** Native reveal runner; a constructor seam for deterministic tests. */
|
|
201
159
|
revealNative;
|
|
160
|
+
/** Native open-in-app runner; a constructor seam for deterministic tests. */
|
|
161
|
+
openExternalNative;
|
|
202
162
|
/** Bash-write collector (S2 seam), or undefined when disabled in config. */
|
|
203
163
|
collector;
|
|
204
164
|
/** Per-session by-turn fold cache, invalidated by the log watermark. */
|
|
@@ -219,6 +179,7 @@ let FilePreviewService = (() => {
|
|
|
219
179
|
captureBashWrites: config.captureBashWrites ?? true,
|
|
220
180
|
};
|
|
221
181
|
this.revealNative = deps.revealNative ?? revealNativePath;
|
|
182
|
+
this.openExternalNative = deps.openExternalNative ?? openExternalNative;
|
|
222
183
|
if (config.captureBashWrites !== false) {
|
|
223
184
|
this.collector = new BashWriteCollector({ fs: this.fs, maxFiles: this.resolved.maxFiles });
|
|
224
185
|
ctx.effect(() => this.collector.attach(ctx), 'file-preview: bash-write capture');
|
|
@@ -239,7 +200,10 @@ let FilePreviewService = (() => {
|
|
|
239
200
|
handler: (req, res) => { void this.serveImage(req, res); },
|
|
240
201
|
}), 'file-preview: image route');
|
|
241
202
|
}
|
|
242
|
-
|
|
203
|
+
}
|
|
204
|
+
/** Zero-session availability handshake for browser companions. */
|
|
205
|
+
capabilities() {
|
|
206
|
+
return { protocolVersion: 1 };
|
|
243
207
|
}
|
|
244
208
|
get fs() {
|
|
245
209
|
return this.ctx.fs;
|
|
@@ -385,7 +349,7 @@ let FilePreviewService = (() => {
|
|
|
385
349
|
/**
|
|
386
350
|
* Every turn's file mutations for the turn-tail card — the single source of
|
|
387
351
|
* truth the card renders (the client's old write/edit-only fold retired).
|
|
388
|
-
* Built by folding the session log per turn (write/edit calls,
|
|
352
|
+
* Built by folding the session log per turn (write/edit calls, PTC
|
|
389
353
|
* dispatches borrowing the root call's turn, and render-intent paths from
|
|
390
354
|
* result diff meta) and merging the collector's bash-written captures by
|
|
391
355
|
* their own turn. The fold is cached per session and invalidated by the log
|
|
@@ -446,7 +410,10 @@ let FilePreviewService = (() => {
|
|
|
446
410
|
}
|
|
447
411
|
/**
|
|
448
412
|
* Read the current content of one recorded file, resolved against the
|
|
449
|
-
* session cwd and capped by `maxReadBytes`.
|
|
413
|
+
* session cwd and capped by `maxReadBytes`. The text read is a bounded byte
|
|
414
|
+
* window (`readByteRange` of cap + 1 bytes), so an oversized file costs the
|
|
415
|
+
* cap, never its full size; a known oversized stat still short-circuits to
|
|
416
|
+
* `too-large` before any bytes move.
|
|
450
417
|
* @param agent - owning live agent; its session cwd anchors relative paths.
|
|
451
418
|
* @param path - the display path recorded by the write/edit tool call.
|
|
452
419
|
* @param signal - cooperative cancellation from the calling UI request.
|
|
@@ -500,15 +467,18 @@ let FilePreviewService = (() => {
|
|
|
500
467
|
return { path, kind: 'binary', ...(info.size === undefined ? {} : { size: info.size }) };
|
|
501
468
|
}
|
|
502
469
|
try {
|
|
503
|
-
|
|
470
|
+
// Bounded by the window, not the file: at most cap + 1 bytes are
|
|
471
|
+
// transferred (the extra byte decides `truncated`), never the whole file.
|
|
472
|
+
const bytes = await this.fs.readByteRange(target, { offset: 0, length: cap + 1 }, signal);
|
|
473
|
+
const truncated = bytes.length > cap;
|
|
474
|
+
const content = utf8Decoder.decode(truncated ? bytes.subarray(0, cap) : bytes);
|
|
504
475
|
if (hasNulByte(content)) {
|
|
505
476
|
return { path, kind: 'binary', ...(info.size === undefined ? {} : { size: info.size }) };
|
|
506
477
|
}
|
|
507
|
-
const truncated = content.length > cap;
|
|
508
478
|
return {
|
|
509
479
|
path,
|
|
510
480
|
kind: 'text',
|
|
511
|
-
content
|
|
481
|
+
content,
|
|
512
482
|
truncated,
|
|
513
483
|
...(html && isScriptedHtml(content) ? { htmlScripted: true } : {}),
|
|
514
484
|
...(info.size === undefined ? {} : { size: info.size }),
|
|
@@ -567,6 +537,53 @@ let FilePreviewService = (() => {
|
|
|
567
537
|
return { revealed: false, reason: 'select-failed' };
|
|
568
538
|
}
|
|
569
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Open one recorded file in a specific host application (the "open in IDE"
|
|
542
|
+
* gesture). The official open-in-app route only accepts directories — a
|
|
543
|
+
* file path 404s — so file-exact opens go through this method: macOS
|
|
544
|
+
* `open -a <App> <path>` via the same native-command runner reveal uses.
|
|
545
|
+
* The browser names the application by official open-in-app catalog id
|
|
546
|
+
* (probed from `/open-in-app/apps`); the id → app-name map lives in
|
|
547
|
+
* open-external.ts. Non-macOS hosts and unknown ids answer without
|
|
548
|
+
* launching, and the client hides the gesture.
|
|
549
|
+
* @param agent - owning live agent; its session cwd anchors relative paths.
|
|
550
|
+
* @param path - the display path recorded by the read/write/edit tool call.
|
|
551
|
+
* @param app - official open-in-app catalog id (e.g. `cursor`).
|
|
552
|
+
* @param signal - cooperative cancellation from the calling UI request.
|
|
553
|
+
* @returns whether the application launched, or why not.
|
|
554
|
+
*/
|
|
555
|
+
async openExternal(agent, path, app, signal) {
|
|
556
|
+
if (typeof path !== 'string' || path.length === 0 || typeof app !== 'string' || app.length === 0) {
|
|
557
|
+
return { opened: false, reason: 'missing' };
|
|
558
|
+
}
|
|
559
|
+
if (macAppName(app) === undefined)
|
|
560
|
+
return { opened: false, reason: 'unknown-app' };
|
|
561
|
+
const cwd = agent.session.header.cwd;
|
|
562
|
+
let target;
|
|
563
|
+
try {
|
|
564
|
+
target = await this.fs.resolve(path, cwd === undefined ? { signal } : { cwd, signal });
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
return { opened: false, reason: 'missing' };
|
|
568
|
+
}
|
|
569
|
+
let info;
|
|
570
|
+
try {
|
|
571
|
+
info = await this.fs.stat(target, signal);
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
info = undefined;
|
|
575
|
+
}
|
|
576
|
+
if (info === undefined)
|
|
577
|
+
return { opened: false, reason: 'missing' };
|
|
578
|
+
try {
|
|
579
|
+
await this.openExternalNative(this.fs.processPath(target), app, signal);
|
|
580
|
+
return { opened: true };
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
584
|
+
return { opened: false, reason: message.startsWith('open-in-app is unsupported on ') ? 'unsupported-platform' : 'launch-failed' };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
570
587
|
};
|
|
571
588
|
})();
|
|
572
589
|
export { FilePreviewService };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native "open this file in a specific application" dispatch used by the
|
|
3
|
+
* file-preview Remote's `openExternal` method (the "open in IDE" gesture).
|
|
4
|
+
*
|
|
5
|
+
* The official open-in-app route (`POST /open-in-app/open`) only accepts
|
|
6
|
+
* directories — a file path 404s — so file-exact opens carry their own
|
|
7
|
+
* dispatch here. macOS only: `open -a <App> <path>` through the official
|
|
8
|
+
* `@deepseek-ai/dsh-native-command` runner (no shell, abort propagation),
|
|
9
|
+
* mirroring `reveal.ts`'s discipline. The browser picks the application by
|
|
10
|
+
* official open-in-app catalog id (probed from `/open-in-app/apps`); the
|
|
11
|
+
* id → `.app` name map below mirrors the official catalog's darwin entries
|
|
12
|
+
* (packages/host/open-in-app/src/catalog.ts at 0.1.5-rc.1 — re-check on every
|
|
13
|
+
* host upgrade). Other platforms and unknown ids answer unsupported, and the
|
|
14
|
+
* client hides the gesture.
|
|
15
|
+
* @module @khorsheed/dsh-file-preview
|
|
16
|
+
*/
|
|
17
|
+
import { type NativeCommandRunner } from '@deepseek-ai/dsh-native-command';
|
|
18
|
+
/** Injectable platform facts for deterministic adapter tests. */
|
|
19
|
+
export interface OpenExternalInternals {
|
|
20
|
+
platform?: NodeJS.Platform;
|
|
21
|
+
run?: NativeCommandRunner;
|
|
22
|
+
}
|
|
23
|
+
/** The application name one catalog id maps to, or undefined when unknown. */
|
|
24
|
+
export declare function macAppName(appId: string): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Open one file in the named application. Throws when the platform is not
|
|
27
|
+
* macOS or the catalog id is unknown — the caller maps both to an
|
|
28
|
+
* `unsupported`/`unknown-app` answer and the client hides the gesture. A
|
|
29
|
+
* launch failure propagates as the runner's rejection.
|
|
30
|
+
* @param path - absolute path in the host's execution world (caller owns
|
|
31
|
+
* resolution, typically `FileSystem.processPath`).
|
|
32
|
+
* @param appId - official open-in-app catalog id (e.g. `cursor`).
|
|
33
|
+
* @param signal - caller/connection lifetime; abort terminates the native command.
|
|
34
|
+
* @param internals - platform seam for deterministic tests.
|
|
35
|
+
*/
|
|
36
|
+
export declare function openExternalNative(path: string, appId: string, signal: AbortSignal, internals?: OpenExternalInternals): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=open-external.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native "open this file in a specific application" dispatch used by the
|
|
3
|
+
* file-preview Remote's `openExternal` method (the "open in IDE" gesture).
|
|
4
|
+
*
|
|
5
|
+
* The official open-in-app route (`POST /open-in-app/open`) only accepts
|
|
6
|
+
* directories — a file path 404s — so file-exact opens carry their own
|
|
7
|
+
* dispatch here. macOS only: `open -a <App> <path>` through the official
|
|
8
|
+
* `@deepseek-ai/dsh-native-command` runner (no shell, abort propagation),
|
|
9
|
+
* mirroring `reveal.ts`'s discipline. The browser picks the application by
|
|
10
|
+
* official open-in-app catalog id (probed from `/open-in-app/apps`); the
|
|
11
|
+
* id → `.app` name map below mirrors the official catalog's darwin entries
|
|
12
|
+
* (packages/host/open-in-app/src/catalog.ts at 0.1.5-rc.1 — re-check on every
|
|
13
|
+
* host upgrade). Other platforms and unknown ids answer unsupported, and the
|
|
14
|
+
* client hides the gesture.
|
|
15
|
+
* @module @khorsheed/dsh-file-preview
|
|
16
|
+
*/
|
|
17
|
+
import { runNativeCommand } from '@deepseek-ai/dsh-native-command';
|
|
18
|
+
/** Official catalog id → macOS application name (darwin entries of the official catalog). */
|
|
19
|
+
const MAC_APP_NAMES = {
|
|
20
|
+
cursor: 'Cursor',
|
|
21
|
+
vscode: 'Visual Studio Code',
|
|
22
|
+
vscodeinsiders: 'Visual Studio Code - Insiders',
|
|
23
|
+
windsurf: 'Windsurf',
|
|
24
|
+
zed: 'Zed',
|
|
25
|
+
sublimetext: 'Sublime Text',
|
|
26
|
+
xcode: 'Xcode',
|
|
27
|
+
androidstudio: 'Android Studio',
|
|
28
|
+
intellij: 'IntelliJ IDEA',
|
|
29
|
+
pycharm: 'PyCharm',
|
|
30
|
+
webstorm: 'WebStorm',
|
|
31
|
+
phpstorm: 'PhpStorm',
|
|
32
|
+
goland: 'GoLand',
|
|
33
|
+
rider: 'Rider',
|
|
34
|
+
rustrover: 'RustRover',
|
|
35
|
+
};
|
|
36
|
+
/** The application name one catalog id maps to, or undefined when unknown. */
|
|
37
|
+
export function macAppName(appId) {
|
|
38
|
+
return MAC_APP_NAMES[appId];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Open one file in the named application. Throws when the platform is not
|
|
42
|
+
* macOS or the catalog id is unknown — the caller maps both to an
|
|
43
|
+
* `unsupported`/`unknown-app` answer and the client hides the gesture. A
|
|
44
|
+
* launch failure propagates as the runner's rejection.
|
|
45
|
+
* @param path - absolute path in the host's execution world (caller owns
|
|
46
|
+
* resolution, typically `FileSystem.processPath`).
|
|
47
|
+
* @param appId - official open-in-app catalog id (e.g. `cursor`).
|
|
48
|
+
* @param signal - caller/connection lifetime; abort terminates the native command.
|
|
49
|
+
* @param internals - platform seam for deterministic tests.
|
|
50
|
+
*/
|
|
51
|
+
export async function openExternalNative(path, appId, signal, internals = {}) {
|
|
52
|
+
const platform = internals.platform ?? process.platform;
|
|
53
|
+
if (platform !== 'darwin') {
|
|
54
|
+
throw new Error(`open-in-app is unsupported on ${platform}`);
|
|
55
|
+
}
|
|
56
|
+
const app = macAppName(appId);
|
|
57
|
+
if (app === undefined)
|
|
58
|
+
throw new Error(`unknown application id "${appId}"`);
|
|
59
|
+
const run = internals.run ?? runNativeCommand;
|
|
60
|
+
await run('open', ['-a', app, path], signal);
|
|
61
|
+
}
|
package/lib/types/types.d.ts
CHANGED
|
@@ -95,6 +95,16 @@ export type FilePreviewReveal = {
|
|
|
95
95
|
/** Why: `missing` = the recorded path does not resolve to an existing target; `select-failed` = the host could not select the file. */
|
|
96
96
|
readonly reason: 'missing' | 'select-failed';
|
|
97
97
|
};
|
|
98
|
+
/** Outcome of a `filePreview.openExternal` call (the "open in IDE" gesture). */
|
|
99
|
+
export type FilePreviewOpenExternal = {
|
|
100
|
+
/** The application launched on the file. */
|
|
101
|
+
readonly opened: true;
|
|
102
|
+
} | {
|
|
103
|
+
/** The file could not be opened in the named application. */
|
|
104
|
+
readonly opened: false;
|
|
105
|
+
/** Why: `missing` = the recorded path does not resolve to an existing target; `unknown-app` = the catalog id is not mapped; `unsupported-platform` = the host is not macOS; `launch-failed` = the native launch rejected. */
|
|
106
|
+
readonly reason: 'missing' | 'unknown-app' | 'unsupported-platform' | 'launch-failed';
|
|
107
|
+
};
|
|
98
108
|
/** One file's mutation facts within one turn — the turn card's vocabulary. */
|
|
99
109
|
export interface FilePreviewTurnFile {
|
|
100
110
|
/** The display path (absolute for bash-captured writes; as-recorded otherwise). */
|
|
@@ -105,7 +115,7 @@ export interface FilePreviewTurnFile {
|
|
|
105
115
|
/** Lines added across the turn's mutations of this path. */
|
|
106
116
|
readonly added?: number;
|
|
107
117
|
/** Lines removed; 0 for a create; absent when an uncounted mutation (a write
|
|
108
|
-
* whose prior content was not diffable, a
|
|
118
|
+
* whose prior content was not diffable, a PTC dispatch) reported no
|
|
109
119
|
* total. */
|
|
110
120
|
readonly removed?: number;
|
|
111
121
|
}
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"cordis",
|
|
8
8
|
"file-preview"
|
|
9
9
|
],
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.3.1",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"types": "lib/types/index.d.ts",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"default": "./lib/typert.remote-client.js"
|
|
34
34
|
},
|
|
35
35
|
"./src/*": "./src/*",
|
|
36
|
+
"./locale/*.json": "./locale/*.json",
|
|
36
37
|
"./package.json": "./package.json"
|
|
37
38
|
},
|
|
38
39
|
"files": [
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
"lib/typert.remote-client.d.ts",
|
|
43
44
|
"lib/typert.remote-client.js",
|
|
44
45
|
"cordis.patch.yml",
|
|
45
|
-
"
|
|
46
|
+
"locale/*.json",
|
|
46
47
|
"LICENSE",
|
|
47
48
|
"CHANGELOG.md"
|
|
48
49
|
],
|
|
@@ -57,27 +58,16 @@
|
|
|
57
58
|
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
|
|
58
59
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
59
60
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
|
|
63
|
-
"@deepseek-ai/dsh-fs": "^0.1.2-rc.1",
|
|
64
|
-
"@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
|
|
65
|
-
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
66
|
-
"@deepseek-ai/dsh-loader-smoke": "^0.1.2-rc.1",
|
|
67
|
-
"@deepseek-ai/dsh-native-command": "^0.1.2-rc.1",
|
|
68
|
-
"@deepseek-ai/dsh-session": "^0.1.2-rc.1",
|
|
69
|
-
"@deepseek-ai/dsh-skill": "^0.1.2-rc.1",
|
|
70
|
-
"@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
|
|
71
|
-
"@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
|
|
72
|
-
"@deepseek-ai/dsh-util-values": "^0.1.2-rc.1"
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"zod": "^4.4.3"
|
|
73
63
|
},
|
|
74
64
|
"dsh": {
|
|
75
65
|
"bundle": {
|
|
76
66
|
"patch": "./cordis.patch.yml"
|
|
77
67
|
},
|
|
78
68
|
"compat": {
|
|
79
|
-
"minHost": "0.1.
|
|
80
|
-
"verifiedHost": "0.1.
|
|
69
|
+
"minHost": "0.1.5-rc.1",
|
|
70
|
+
"verifiedHost": "0.1.5-rc.1"
|
|
81
71
|
}
|
|
82
72
|
}
|
|
83
73
|
}
|