dsh-plugin-lcu 0.2.9 → 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.
@@ -2,7 +2,7 @@
2
2
  * `dsh-plugin-lcu` — drive the desktop and Chrome from DeepSeek Harness.
3
3
  *
4
4
  * The plugin is one root-scoped row. It does not open anything at load time:
5
- * LCU (and the CUA runtime behind it) starts only for an Agent whose preset is
5
+ * The runtime starts only for an Agent whose preset is
6
6
  * listed in `presets`, and stops when that Agent is disposed. That keeps a
7
7
  * heavy, permission-bearing capability off every session that does not need it.
8
8
  *
@@ -19,11 +19,23 @@ export declare const name = "lcu";
19
19
  export declare const inject: string[];
20
20
  /** Plugin configuration. */
21
21
  export interface Config {
22
- /** LCU launcher; defaults to the installed path, overridable for a custom prefix. */
22
+ /**
23
+ * The ChatGPT application whose runtime provides computer use.
24
+ *
25
+ * Defaults to the standard installation. The plugin launches the runtime from
26
+ * inside this application directly — it does not go through a wrapper.
27
+ */
28
+ app?: string;
29
+ /**
30
+ * An explicit executable replacing the computed launch, for an unusual install.
31
+ *
32
+ * Set this only when the application is somewhere the built-in resolution
33
+ * cannot reach; it bypasses the runtime's own environment setup.
34
+ */
23
35
  command?: string;
24
- /** Enable the Chrome surface (`lcu --chrome`); needs the extension and site approvals. */
36
+ /** Enable the runtime's browser surface; needs the official browser extension and site approvals. */
25
37
  chrome?: boolean;
26
- /** Enable the original computer-audio recording API (`lcu --audio`). */
38
+ /** Enable the runtime's computer-audio recording API. */
27
39
  audio?: boolean;
28
40
  /**
29
41
  * Agent preset ids whose sessions get the tools.
@@ -35,11 +47,19 @@ export interface Config {
35
47
  presets?: string[];
36
48
  /** Exact HTTP(S) origins pre-approved for browser access, never widened. */
37
49
  allowedOrigins?: string[];
38
- /** Prompt section order for the injected LCU instructions. */
50
+ /**
51
+ * Bundle identifiers computer use may use without asking.
52
+ *
53
+ * The runtime asks before it first uses each application, and an unanswered
54
+ * question is a refusal, so an unattended run needs its targets listed here.
55
+ * The application hosting the agent can never be admitted this way.
56
+ */
57
+ allowedApps?: string[];
58
+ /** Prompt section order for the injected runtime instructions. */
39
59
  sectionOrder?: number;
40
60
  }
41
61
  /**
42
- * Mount the LCU capability.
62
+ * Mount the computer-use capability.
43
63
  *
44
64
  * @param ctx - the host context this row mounts into.
45
65
  * @param config - the row's configuration.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,EAC+C,kBAAkB,EAEvE,MAAM,iBAAiB,CAAA;AAOxB,eAAO,MAAM,IAAI,QAAQ,CAAA;AACzB,eAAO,MAAM,MAAM,UAA4B,CAAA;AA2B/C,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AA2BD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgSxD;AAYD,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAQlD,OAAO,EAC+C,kBAAkB,EAEvE,MAAM,iBAAiB,CAAA;AAUxB,eAAO,MAAM,IAAI,QAAQ,CAAA;AACzB,eAAO,MAAM,MAAM,UAA4B,CAAA;AAyB/C,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AA8BD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAyWxD;AAYD,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The connection an Agent's tools talk to.
3
+ *
4
+ * The runtime is executed from files inside the ChatGPT application, and the
5
+ * application replaces those files when it updates. A session that stays
6
+ * connected across an update therefore runs a mix of two generations — the
7
+ * runtime it started with and the modules a later `import` resolves from the new
8
+ * bundle — and the failure that follows is not obvious from the outside.
9
+ *
10
+ * This is a thin facade rather than a fix inside the connection: the tools close
11
+ * over one object for the life of the Agent, so that object has to be able to
12
+ * become a different connection. Before every call it compares the application's
13
+ * fingerprint, and when it changed it opens a new connection and retires the old
14
+ * one, so all later calls come from a single generation.
15
+ *
16
+ * @module dsh-plugin-lcu/session
17
+ */
18
+ import type { LcuConnection } from './connection.ts';
19
+ /**
20
+ * What the tools need from a connection.
21
+ *
22
+ * Written as indexed access types rather than restated signatures, so a change to
23
+ * the connection cannot leave this out of date.
24
+ */
25
+ export interface RuntimeConnection {
26
+ sessionId: string | undefined;
27
+ modelTools: LcuConnection['modelTools'];
28
+ readonly hasHostControl: boolean;
29
+ callTool: LcuConnection['callTool'];
30
+ turnEnded: LcuConnection['turnEnded'];
31
+ controlStatus: LcuConnection['controlStatus'];
32
+ controlStop: LcuConnection['controlStop'];
33
+ close: LcuConnection['close'];
34
+ }
35
+ /** How the session opens a connection, and how it notices the application moved. */
36
+ export interface ManagedSessionOptions {
37
+ /**
38
+ * Open a fresh connection.
39
+ *
40
+ * @param reason - whether this is the first connection or a replacement.
41
+ */
42
+ connect: (reason: 'initial' | 'regenerated') => Promise<LcuConnection>;
43
+ /**
44
+ * The application's current fingerprint.
45
+ *
46
+ * Called before every call, so it must stay cheap: it reads two file
47
+ * timestamps. It may throw, which is treated as "the application moved" and
48
+ * lets the reconnect report the real error.
49
+ */
50
+ generation: () => string;
51
+ /** Called after a successful replacement, for the diagnostic log. */
52
+ onRegenerated?: (transition: {
53
+ readonly from: string;
54
+ readonly to: string;
55
+ }) => void;
56
+ /** Called when a replacement fails. The previous connection stays in use. */
57
+ onRegenerateFailed?: (error: unknown) => void;
58
+ /**
59
+ * Called when the fingerprint cannot be read at all.
60
+ *
61
+ * A fingerprint that cannot be read is **not** evidence that anything changed,
62
+ * and opening a second runtime because of a transient read failure is worse
63
+ * than waiting: the next call checks again.
64
+ */
65
+ onGenerationUnreadable?: (error: unknown) => void;
66
+ }
67
+ /** A connection that follows the application across an update. */
68
+ export declare class ManagedSession implements RuntimeConnection {
69
+ #private;
70
+ private constructor();
71
+ /**
72
+ * Open the first connection.
73
+ *
74
+ * @param options - how to connect and how to notice a change.
75
+ * @returns the session, already connected.
76
+ */
77
+ static open(options: ManagedSessionOptions): Promise<ManagedSession>;
78
+ get sessionId(): string | undefined;
79
+ set sessionId(value: string | undefined);
80
+ get hasHostControl(): boolean;
81
+ modelTools(): ReturnType<LcuConnection['modelTools']>;
82
+ callTool(...args: Parameters<LcuConnection['callTool']>): ReturnType<LcuConnection['callTool']>;
83
+ turnEnded(...args: Parameters<LcuConnection['turnEnded']>): ReturnType<LcuConnection['turnEnded']>;
84
+ controlStatus(...args: Parameters<LcuConnection['controlStatus']>): ReturnType<LcuConnection['controlStatus']>;
85
+ controlStop(...args: Parameters<LcuConnection['controlStop']>): ReturnType<LcuConnection['controlStop']>;
86
+ /** Retire the connection. Later calls fail rather than opening a new runtime. */
87
+ close(): Promise<void>;
88
+ }
89
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;IACvC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IACnC,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IACrC,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,CAAA;IAC7C,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACzC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAC9B;AAED,oFAAoF;AACpF,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;IACtE;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,MAAM,CAAA;IACxB,qEAAqE;IACrE,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACpF,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAClD;AAED,kEAAkE;AAClE,qBAAa,cAAe,YAAW,iBAAiB;;IAgBtD,OAAO;IAMP;;;;;OAKG;WACU,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;IAc1E,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAGtC;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,UAAU,IAAI,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IA2C/C,QAAQ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAK/F,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,GAAG,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAKlG,aAAa,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,GAAG,UAAU,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAK9G,WAAW,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,GAAG,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IAK9G,iFAAiF;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
@@ -12,7 +12,8 @@
12
12
  */
13
13
  import type { Context } from '@deepseek-ai/cordis';
14
14
  import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
15
- import type { LcuConnection, LcuContentBlock, LcuTool } from './connection.ts';
15
+ import type { LcuContentBlock, LcuTool } from './connection.ts';
16
+ import type { RuntimeConnection } from './session.ts';
16
17
  interface ImageBlock {
17
18
  readonly type: 'image';
18
19
  readonly data: string;
@@ -96,7 +97,7 @@ export declare function stuckStopHint(text: string): string | undefined;
96
97
  * @param options - host hooks around the call.
97
98
  * @returns a registrable tool definition.
98
99
  */
99
- export declare function createLcuTool(descriptor: LcuTool, connection: LcuConnection, ctx: Context, options?: LcuToolOptions): ToolDefinition;
100
+ export declare function createLcuTool(descriptor: LcuTool, connection: RuntimeConnection, ctx: Context, options?: LcuToolOptions): ToolDefinition;
100
101
  /**
101
102
  * Build every model-facing tool for one agent's connection.
102
103
  *
@@ -105,7 +106,7 @@ export declare function createLcuTool(descriptor: LcuTool, connection: LcuConnec
105
106
  * @returns tool definitions for `js` and `js_reset`, in server order.
106
107
  * @throws {LcuError} when the server does not expose the model tools.
107
108
  */
108
- export declare function buildLcuTools(connection: LcuConnection, ctx: Context, options?: LcuToolOptions): ToolDefinition[];
109
+ export declare function buildLcuTools(connection: RuntimeConnection, ctx: Context, options?: LcuToolOptions): ToolDefinition[];
109
110
  /**
110
111
  * The explicit per-app release, which LCU exposes to a host rather than to the
111
112
  * model.
@@ -119,7 +120,7 @@ export declare function buildLcuTools(connection: LcuConnection, ctx: Context, o
119
120
  * @param currentTurn - resolves the agent's active turn, or `undefined` outside one.
120
121
  * @returns a registrable tool definition.
121
122
  */
122
- export declare function createComputerUseStopTool(connection: LcuConnection, currentTurn: () => string | undefined): ToolDefinition;
123
+ export declare function createComputerUseStopTool(connection: RuntimeConnection, currentTurn: () => string | undefined): ToolDefinition;
123
124
  /**
124
125
  * Register the model-facing tools in one agent's scope.
125
126
  *
@@ -132,6 +133,6 @@ export declare function createComputerUseStopTool(connection: LcuConnection, cur
132
133
  * @param ctx - the plugin context.
133
134
  * @returns a disposer that unregisters every tool.
134
135
  */
135
- export declare function registerLcuTools(agentCtx: Context, connection: LcuConnection, ctx: Context, options?: LcuToolOptions): () => void;
136
+ export declare function registerLcuTools(agentCtx: Context, connection: RuntimeConnection, ctx: Context, options?: LcuToolOptions): () => void;
136
137
  export {};
137
138
  //# sourceMappingURL=tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAsD,MAAM,wBAAwB,CAAA;AAEhH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAU9E,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWzF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQlF;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGtE;AA2GD,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAO5E;AAWD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWtE;AAcD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,aAAa,EACzB,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,cAAc,CA+DhB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,aAAa,EACzB,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,cAAc,EAAE,CAOlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,aAAa,EACzB,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,GACpC,cAAc,CAiEhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,aAAa,EACzB,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,MAAM,IAAI,CASZ"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAsD,MAAM,wBAAwB,CAAA;AAEhH,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAUrD,UAAU,UAAU;IAClB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWzF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQlF;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGtE;AA2GD,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;CAChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAO5E;AAWD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWtE;AAcD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,iBAAiB,EAC7B,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,cAAc,CA+DhB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,iBAAiB,EAC7B,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,cAAc,EAAE,CAOlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,iBAAiB,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,GACpC,cAAc,CAsEhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,iBAAiB,EAC7B,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,cAAmB,GAC3B,MAAM,IAAI,CASZ"}
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "dsh-plugin-lcu",
3
- "version": "0.2.9",
4
- "description": "Drive the desktop and Chrome from DeepSeek Harness through LCU (Codex computer use, decoupled from the app).",
3
+ "version": "0.3.0",
4
+ "description": "Drive the desktop and Chrome from DeepSeek Harness using the computer-use runtime inside the ChatGPT desktop application.",
5
5
  "keywords": [
6
6
  "dsh-plugin",
7
7
  "deepseek-harness",
8
8
  "dsh",
9
9
  "computer-use",
10
10
  "browser-use",
11
- "lcu",
12
- "codex",
13
- "cua"
11
+ "cua",
12
+ "chatgpt",
13
+ "desktop-automation",
14
+ "macos"
14
15
  ],
15
16
  "license": "MIT",
16
17
  "author": {
@@ -37,6 +38,7 @@
37
38
  },
38
39
  "files": [
39
40
  "lib",
41
+ "helper",
40
42
  "cordis.patch.yml",
41
43
  "scripts",
42
44
  "dev-types",
@@ -21,7 +21,8 @@
21
21
  * node scripts/gen-presets.mjs # write into the desktop profile patch
22
22
  * node scripts/gen-presets.mjs --dry-run # print, write nothing
23
23
  * node scripts/gen-presets.mjs --out FILE # write to FILE instead
24
- * node scripts/gen-presets.mjs --with-heavy # also emit preset-heavy (needs dsh-plugin-lcu)
24
+ * node scripts/gen-presets.mjs # emits daily and heavy
25
+ * node scripts/gen-presets.mjs --daily-only # refuses if the file already has heavy
25
26
  * node scripts/gen-presets.mjs --asar PATH # override the app.asar path
26
27
  * node scripts/gen-presets.mjs --profile DIR # override the profile directory
27
28
  */
@@ -205,7 +206,11 @@ function renderPreset(spec, basePlugins) {
205
206
  function parseArgs(argv) {
206
207
  const opts = {
207
208
  dryRun: false,
208
- withHeavy: false,
209
+ // Default to the full set: this script ships with dsh-plugin-lcu, and
210
+ // `heavy` is the preset that attaches it. Defaulting to daily-only meant a
211
+ // routine re-run after a DSH upgrade silently deleted the preset computer
212
+ // use depends on.
213
+ withHeavy: true,
209
214
  out: undefined,
210
215
  asar: DEFAULT_ASAR,
211
216
  profile: DEFAULT_PROFILE,
@@ -214,6 +219,7 @@ function parseArgs(argv) {
214
219
  const arg = argv[i]
215
220
  if (arg === '--dry-run') opts.dryRun = true
216
221
  else if (arg === '--with-heavy') opts.withHeavy = true
222
+ else if (arg === '--daily-only') opts.withHeavy = false
217
223
  else if (arg === '--out') opts.out = argv[++i]
218
224
  else if (arg === '--asar') opts.asar = argv[++i]
219
225
  else if (arg === '--profile') opts.profile = argv[++i]
@@ -254,6 +260,19 @@ function main() {
254
260
  } else {
255
261
  const target = opts.out ?? join(opts.profile, 'cordis.patch.yml')
256
262
  const before = readFileSync(target, 'utf8')
263
+ // Never delete a preset this file already had. A re-run is the documented
264
+ // response to a DSH upgrade, and it must not be able to take away the mode a
265
+ // user relies on as a side effect of an argument they did not pass.
266
+ for (const preset of ['daily', 'heavy']) {
267
+ const had = before.includes(`- id: preset-${preset}`)
268
+ const keeps = block.includes(`- id: preset-${preset}`)
269
+ if (had && !keeps) {
270
+ throw new Error(
271
+ `${target} already defines preset-${preset} and this run would remove it. `
272
+ + 'Re-run without --daily-only to keep every preset the file has.',
273
+ )
274
+ }
275
+ }
257
276
  writeFileSync(target, spliceBlock(before, block), { mode: 0o600 })
258
277
  process.stderr.write(`${verb} ${target}\n`)
259
278
  }
@@ -1,37 +1,45 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * probe-lcu.mjs — talk to the installed `lcu` MCP server directly.
3
+ * probe-lcu.mjs — talk to the computer-use runtime directly.
4
4
  *
5
- * A diagnostic for `dsh-plugin-lcu` development: it connects the way the plugin
6
- * will (newline-delimited JSON-RPC over stdio, `capabilities.elicitation`
7
- * declared), prints what the server advertises, and optionally calls one tool.
5
+ * A diagnostic for `dsh-plugin-lcu` development: it builds the same launch plan
6
+ * the plugin builds, connects the way the plugin does (newline-delimited
7
+ * JSON-RPC over stdio, `capabilities.elicitation` declared), prints what the
8
+ * server advertises, and optionally calls one tool. No harness is involved, so
9
+ * this separates a plugin problem from a runtime problem.
8
10
  *
9
11
  * Usage
10
12
  * -----
11
- * node scripts/probe-lcu.mjs # initialize + tools/list
12
- * node scripts/probe-lcu.mjs --instructions # also dump server instructions
13
- * node scripts/probe-lcu.mjs --call js --code 'return await cua.listApps()'
14
- * node scripts/probe-lcu.mjs --command /path/to/lcu # override the binary
13
+ * node scripts/probe-lcu.mjs # initialize + tools/list
14
+ * node scripts/probe-lcu.mjs --instructions # also dump server instructions
15
+ * node scripts/probe-lcu.mjs --call js --code 'await cua.getState();'
16
+ * node scripts/probe-lcu.mjs --chrome # enable the browser surface
17
+ * node scripts/probe-lcu.mjs --app /path/ChatGPT.app # a different installation
18
+ * node scripts/probe-lcu.mjs --command /path/binary # bypass the computed launch
15
19
  *
16
20
  * It never answers elicitations, so any call that needs an approval fails closed
17
21
  * exactly like a host that cannot present the request.
18
22
  */
19
23
 
20
24
  import { spawn } from 'node:child_process'
21
- import { homedir } from 'node:os'
22
- import { join } from 'node:path'
23
25
  import { createInterface } from 'node:readline'
24
26
 
25
- const DEFAULT_COMMAND = join(homedir(), '.local/share/lcu/current/bin/lcu')
27
+ import { planLaunch } from '../lib/app.js'
26
28
 
27
29
  /** Newest protocol revision the MCP SDK pair here is expected to negotiate. */
28
30
  const PROTOCOL_VERSION = '2025-06-18'
29
31
 
30
32
  function parseArgs(argv) {
31
- const opts = { command: DEFAULT_COMMAND, instructions: false, call: undefined, code: undefined, limit: 2000 }
33
+ const opts = {
34
+ app: undefined, command: undefined, chrome: false, audio: false,
35
+ instructions: false, call: undefined, code: undefined, limit: 2000,
36
+ }
32
37
  for (let i = 0; i < argv.length; i += 1) {
33
38
  const arg = argv[i]
34
- if (arg === '--command') opts.command = argv[++i]
39
+ if (arg === '--app') opts.app = argv[++i]
40
+ else if (arg === '--command') opts.command = argv[++i]
41
+ else if (arg === '--chrome') opts.chrome = true
42
+ else if (arg === '--audio') opts.audio = true
35
43
  else if (arg === '--instructions') opts.instructions = true
36
44
  else if (arg === '--call') opts.call = argv[++i]
37
45
  else if (arg === '--code') opts.code = argv[++i]
@@ -41,17 +49,17 @@ function parseArgs(argv) {
41
49
  return opts
42
50
  }
43
51
 
44
- /** One request/response session with the LCU MCP server. */
52
+ /** One request/response session with the computer-use runtime. */
45
53
  class LcuSession {
46
- constructor(command) {
54
+ constructor(plan) {
47
55
  this.nextId = 1
48
56
  this.pending = new Map()
49
57
  this.notifications = []
50
58
  this.stderr = ''
51
- this.child = spawn(command, [], { stdio: ['pipe', 'pipe', 'pipe'], env: process.env })
59
+ this.child = spawn(plan.command, [...plan.args], { stdio: ['pipe', 'pipe', 'pipe'], env: plan.env })
52
60
  this.child.stderr.on('data', (chunk) => { this.stderr += chunk.toString() })
53
61
  this.child.on('exit', (code) => {
54
- for (const { reject } of this.pending.values()) reject(new Error(`lcu exited (${code})\n${this.stderr.slice(-800)}`))
62
+ for (const { reject } of this.pending.values()) reject(new Error(`the runtime exited (${code})\n${this.stderr.slice(-800)}`))
55
63
  this.pending.clear()
56
64
  })
57
65
  createInterface({ input: this.child.stdout }).on('line', (line) => this.#onLine(line))
@@ -96,7 +104,21 @@ class LcuSession {
96
104
 
97
105
  async function main() {
98
106
  const opts = parseArgs(process.argv.slice(2))
99
- const session = new LcuSession(opts.command)
107
+ const plan = planLaunch({
108
+ ...(opts.app === undefined ? {} : { appPath: opts.app }),
109
+ ...(opts.command === undefined ? {} : { command: opts.command }),
110
+ chrome: opts.chrome,
111
+ audio: opts.audio,
112
+ identity: 'dsh-plugin-lcu-probe',
113
+ })
114
+ console.log('=== launch plan ===')
115
+ console.log(` app: ${plan.paths.app}`)
116
+ console.log(` version: ${plan.paths.version}`)
117
+ console.log(` runtime: ${plan.paths.runtimeVersion}`)
118
+ console.log(` command: ${plan.command}`)
119
+ console.log(` args: ${JSON.stringify(plan.args)}`)
120
+ console.log(` surfaces: ${String(plan.env.CUA_REPL_ENABLED_SURFACES)}`)
121
+ const session = new LcuSession(plan)
100
122
  try {
101
123
  const initialized = await session.request('initialize', {
102
124
  protocolVersion: PROTOCOL_VERSION,