@devmarketplacenpm/devmp 0.1.1-beta.5

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/lib/ws-run.js ADDED
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ const { createExecutor } = require('./executor');
4
+ const { createCommandRunner } = require('./command-runner');
5
+ const { agentWsUrl: wsUrl } = require('./routes');
6
+ const { record: recordClientVersion } = require('./version');
7
+ const { readProjectInstructions } = require('./instructions');
8
+
9
+ /**
10
+ * Run the agent over the live WebSocket tunnel. The server drives; this client
11
+ * answers on-demand file ops against local disk (via the executor) and forwards
12
+ * text/done/error to `onEvent`. Resolves when the socket closes.
13
+ */
14
+ async function runOverWs({ session, rootDir, prompt, provider, model, maxFiles, yes, allowCommands, onEvent }) {
15
+ // Read before the socket opens: the handshake carries them, and a run that
16
+ // ignored the project's own conventions would have to be corrected by hand.
17
+ const instructions = await readProjectInstructions(rootDir).catch(() => null);
18
+ if (instructions) {
19
+ onEvent({ type: 'instructions', source: instructions.source, truncated: instructions.truncated });
20
+ }
21
+ return new Promise((resolve, reject) => {
22
+ const url = wsUrl(session.apiBaseUrl);
23
+ let ws;
24
+ try {
25
+ ws = new WebSocket(url);
26
+ } catch (error) {
27
+ reject(new Error(`Could not open ${url}: ${error.message}`));
28
+ return;
29
+ }
30
+
31
+ const executor = createExecutor({
32
+ rootDir,
33
+ yes,
34
+ onFile: (path, outcome) => onEvent({ type: 'file-local', path, outcome }),
35
+ });
36
+ const commandRunner = createCommandRunner({ rootDir, allowCommands });
37
+
38
+ let settled = false;
39
+ const done = (fn, arg) => {
40
+ if (settled) return;
41
+ settled = true;
42
+ process.off('SIGINT', onSigint);
43
+ fn(arg);
44
+ };
45
+ const onSigint = () => {
46
+ try {
47
+ ws.send(JSON.stringify({ type: 'cancel' }));
48
+ } catch {
49
+ /* socket already gone */
50
+ }
51
+ };
52
+ process.on('SIGINT', onSigint);
53
+
54
+ ws.onopen = () => {
55
+ ws.send(
56
+ JSON.stringify({
57
+ type: 'start',
58
+ token: session.accessToken,
59
+ prompt,
60
+ ...(provider ? { provider } : {}),
61
+ ...(model ? { model } : {}),
62
+ ...(maxFiles ? { maxFiles } : {}),
63
+ ...(instructions
64
+ ? {
65
+ instructions: instructions.text,
66
+ instructionsSource: instructions.source,
67
+ }
68
+ : {}),
69
+ }),
70
+ );
71
+ };
72
+
73
+ ws.onmessage = async (event) => {
74
+ let msg;
75
+ try {
76
+ const raw =
77
+ typeof event.data === 'string' ? event.data : event.data.toString();
78
+ msg = JSON.parse(raw);
79
+ } catch {
80
+ return;
81
+ }
82
+
83
+ // Additive and transport-agnostic: whichever frame the server chooses
84
+ // to carry version info on, the client picks it up.
85
+ recordClientVersion(msg.client);
86
+
87
+ if (msg.type === 'fs') {
88
+ try {
89
+ const payload = await executor.handle(msg.op, msg.args || {});
90
+ ws.send(JSON.stringify({ type: 'fsResult', id: msg.id, ok: true, ...payload }));
91
+ } catch (error) {
92
+ ws.send(
93
+ JSON.stringify({
94
+ type: 'fsResult',
95
+ id: msg.id,
96
+ ok: false,
97
+ message: error.message,
98
+ }),
99
+ );
100
+ }
101
+ return;
102
+ }
103
+
104
+ if (msg.type === 'cmd') {
105
+ try {
106
+ const payload = await commandRunner.handle(
107
+ String(msg.command || ''),
108
+ msg.id,
109
+ );
110
+ ws.send(JSON.stringify({ type: 'cmdResult', id: msg.id, ok: true, ...payload }));
111
+ } catch (error) {
112
+ ws.send(
113
+ JSON.stringify({
114
+ type: 'cmdResult',
115
+ id: msg.id,
116
+ ok: false,
117
+ message: error.message,
118
+ }),
119
+ );
120
+ }
121
+ return;
122
+ }
123
+
124
+ if (msg.type === 'cmdCancel') {
125
+ commandRunner.cancel(msg.id);
126
+ return;
127
+ }
128
+
129
+ onEvent(msg); // text | done | error
130
+ };
131
+
132
+ ws.onerror = (event) => {
133
+ const message =
134
+ (event && (event.message || (event.error && event.error.message))) ||
135
+ 'connection failed';
136
+ done(reject, new Error(`Could not reach the agent tunnel at ${url} (${message}).`));
137
+ };
138
+ ws.onclose = () => done(resolve);
139
+ });
140
+ }
141
+
142
+ module.exports = { runOverWs };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@devmarketplacenpm/devmp",
3
+ "version": "0.1.1-beta.5",
4
+ "description": "DevMarketplace CLI — a terminal coding agent. The agent runs on the server; the files land on your machine.",
5
+ "keywords": [
6
+ "cli",
7
+ "coding-agent",
8
+ "ai",
9
+ "terminal",
10
+ "devmarketplace"
11
+ ],
12
+ "homepage": "https://github.com/gentez/devmarketplace-cli#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/gentez/devmarketplace-cli/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/gentez/devmarketplace-cli.git"
19
+ },
20
+ "bin": {
21
+ "devmp": "bin/devmp.js"
22
+ },
23
+ "files": [
24
+ "bin",
25
+ "lib",
26
+ "README.md"
27
+ ],
28
+ "type": "commonjs",
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "scripts": {
33
+ "devmp": "node bin/devmp.js",
34
+ "test": "node --test test/*.test.js"
35
+ },
36
+ "license": "UNLICENSED",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }