@aryacode/dsh-plugin-arya-computer-use 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/index.js +81 -2
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -18,8 +18,10 @@
18
18
 
19
19
  import z from '@deepseek-ai/schemastery';
20
20
  import os from 'node:os';
21
- import { readFile, readFileSync, readdirSync } from 'node:fs';
21
+ import { readFile, readFileSync, readdirSync, statSync } from 'node:fs';
22
+ import { execFileSync } from 'node:child_process';
22
23
  import { dirname, join } from 'node:path';
24
+ import { createRequire } from 'node:module';
23
25
  import { fileURLToPath, pathToFileURL } from 'node:url';
24
26
  import { DEFAULT_SETTINGS, normalizeSettings, STOP_SHORTCUT_PATTERN, repairStaleSettings } from './mcp/settings.mjs';
25
27
  import { TakeoverControl, atomicJson } from './mcp/takeover.mjs';
@@ -84,6 +86,68 @@ async function loadMcpBridge() {
84
86
  return { bridge: null, resolveErrors };
85
87
  }
86
88
 
89
+ // ---------------------------------------------------------------------------
90
+ // Engine binaries.
91
+ //
92
+ // agent-desktop and agent-browser ship their native binary through a
93
+ // postinstall script. pnpm (and therefore the plugin manager) refuses to run
94
+ // dependency build scripts unless they are approved, so a market install can
95
+ // leave both engines without a binary. Rather than fail with an opaque spawn
96
+ // error much later, run the vendor's own postinstall once, ourselves, and
97
+ // report what happened in the status route.
98
+ // ---------------------------------------------------------------------------
99
+
100
+ const ENGINE_PACKAGES = ['agent-desktop', 'agent-browser'];
101
+
102
+ function engineRoot(pkg) {
103
+ try {
104
+ return dirname(createRequire(import.meta.url).resolve(`${pkg}/package.json`));
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ function engineBinaryReady(root) {
111
+ const binDir = join(root, 'bin');
112
+ const token = `${process.platform}-${process.arch}`;
113
+ const wantSuffix = process.platform === 'win32' ? '.exe' : '';
114
+ try {
115
+ return readdirSync(binDir).some(name => {
116
+ if (name.endsWith('.sha256') || !name.includes(token)) return false;
117
+ return name.slice(name.indexOf(token) + token.length) === wantSuffix;
118
+ });
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+
124
+ async function ensureEngine(pkg) {
125
+ const root = engineRoot(pkg);
126
+ if (!root) return { package: pkg, ok: false, reason: 'not-installed' };
127
+ if (engineBinaryReady(root)) return { package: pkg, ok: true, binary: 'present' };
128
+ const script = join(root, 'scripts', 'postinstall.js');
129
+ try {
130
+ if (!statSync(script).isFile()) return { package: pkg, ok: false, reason: 'no-postinstall' };
131
+ } catch {
132
+ return { package: pkg, ok: false, reason: 'no-postinstall' };
133
+ }
134
+ try {
135
+ execFileSync(process.execPath, [script], { cwd: root, stdio: 'ignore', timeout: 600000, windowsHide: true });
136
+ } catch (error) {
137
+ return { package: pkg, ok: false, reason: 'postinstall-failed', message: error?.message ?? String(error) };
138
+ }
139
+ return { package: pkg, ok: engineBinaryReady(root), binary: 'downloaded' };
140
+ }
141
+
142
+ async function ensureEngines() {
143
+ const results = [];
144
+ for (const pkg of ENGINE_PACKAGES) results.push(await ensureEngine(pkg));
145
+ return results;
146
+ }
147
+
148
+ // Internal hook so a diagnostic can exercise the repair without a host.
149
+ export { ensureEngines as ensureEngineBinaries };
150
+
87
151
  // One in-process mcp-client per lane. stdio transport with our own files, so
88
152
  // the same code path serves a checkout and a registry install.
89
153
  async function mountMcpServers(ctx) {
@@ -196,7 +260,22 @@ Object.defineProperty(Config, '~standard', { value: { ...validation, validate(in
196
260
 
197
261
  export async function apply(ctx, config) {
198
262
  registerSkillProvider(ctx);
199
- void mountMcpServers(ctx);
263
+ void (async () => {
264
+ try {
265
+ const engines = await ensureEngines();
266
+ mcpStatus = { ...mcpStatus, engines };
267
+ const broken = engines.filter(engine => !engine.ok);
268
+ if (broken.length) {
269
+ ctx.logger.warn(
270
+ 'dsh-arya-computer-use: engine binaries are missing: %s (run the vendor postinstall, or approve dependency build scripts in the profile)',
271
+ broken.map(engine => `${engine.package}${engine.reason ? ` (${engine.reason})` : ''}`).join(', '),
272
+ );
273
+ }
274
+ } catch (error) {
275
+ ctx.logger.warn('dsh-arya-computer-use: could not prepare engine binaries: %s', error?.message ?? String(error));
276
+ }
277
+ await mountMcpServers(ctx);
278
+ })();
200
279
 
201
280
  const root = controlDir();
202
281
  const runtime = { configFile: join(root, 'settings.json'), stateFile: join(root, 'state.json'), activityFile: join(root, 'activity.json'), layoutFile: join(root, 'overlay-layout.json'), targetFile: join(root, 'target.json') };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryacode/dsh-plugin-arya-computer-use",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Computer use for DSH/AryaCode: agent-desktop UIA engine, agent-browser web lane, takeover UI with a ghost cursor",
5
5
  "license": "MIT",
6
6
  "type": "module",