@cjhyy/code-shell-server 0.8.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.
- package/README.md +77 -0
- package/dist/attachment-service.d.ts +80 -0
- package/dist/attachment-service.js +587 -0
- package/dist/bin/code-shell-serve.d.ts +2 -0
- package/dist/bin/code-shell-serve.js +7 -0
- package/dist/client-message-id.d.ts +1 -0
- package/dist/client-message-id.js +8 -0
- package/dist/image-byte-probe.d.ts +17 -0
- package/dist/image-byte-probe.js +330 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.mobile-remote.d.ts +21 -0
- package/dist/index.mobile-remote.js +21 -0
- package/dist/index.serve.d.ts +3 -0
- package/dist/index.serve.js +3 -0
- package/dist/index.storage.d.ts +10 -0
- package/dist/index.storage.js +10 -0
- package/dist/index.worker.d.ts +2 -0
- package/dist/index.worker.js +2 -0
- package/dist/mobile-remote/access-passcode.d.ts +80 -0
- package/dist/mobile-remote/access-passcode.js +354 -0
- package/dist/mobile-remote/cloudflared-binary.d.ts +76 -0
- package/dist/mobile-remote/cloudflared-binary.js +242 -0
- package/dist/mobile-remote/codex-parse.d.ts +34 -0
- package/dist/mobile-remote/codex-parse.js +131 -0
- package/dist/mobile-remote/codex-room-agent.d.ts +94 -0
- package/dist/mobile-remote/codex-room-agent.js +213 -0
- package/dist/mobile-remote/mobile-attachments.d.ts +21 -0
- package/dist/mobile-remote/mobile-attachments.js +119 -0
- package/dist/mobile-remote/mobile-chat-turn.d.ts +32 -0
- package/dist/mobile-remote/mobile-chat-turn.js +67 -0
- package/dist/mobile-remote/mobile-history.d.ts +22 -0
- package/dist/mobile-remote/mobile-history.js +27 -0
- package/dist/mobile-remote/mobile-run-dispatch.d.ts +17 -0
- package/dist/mobile-remote/mobile-run-dispatch.js +56 -0
- package/dist/mobile-remote/mobile-static.d.ts +43 -0
- package/dist/mobile-remote/mobile-static.js +154 -0
- package/dist/mobile-remote/mobile-upload-service.d.ts +57 -0
- package/dist/mobile-remote/mobile-upload-service.js +370 -0
- package/dist/mobile-remote/pairing.d.ts +8 -0
- package/dist/mobile-remote/pairing.js +23 -0
- package/dist/mobile-remote/path-bins.d.ts +7 -0
- package/dist/mobile-remote/path-bins.js +16 -0
- package/dist/mobile-remote/pending-approvals.d.ts +14 -0
- package/dist/mobile-remote/pending-approvals.js +54 -0
- package/dist/mobile-remote/remote-host-manager.d.ts +117 -0
- package/dist/mobile-remote/remote-host-manager.js +345 -0
- package/dist/mobile-remote/resident-agent.d.ts +98 -0
- package/dist/mobile-remote/resident-agent.js +216 -0
- package/dist/mobile-remote/room-manager.d.ts +286 -0
- package/dist/mobile-remote/room-manager.js +765 -0
- package/dist/mobile-remote/trusted-device-store.d.ts +19 -0
- package/dist/mobile-remote/trusted-device-store.js +118 -0
- package/dist/mobile-remote/tunnel-manager.d.ts +105 -0
- package/dist/mobile-remote/tunnel-manager.js +375 -0
- package/dist/mobile-remote/types.d.ts +1 -0
- package/dist/mobile-remote/types.js +1 -0
- package/dist/mobile-remote/viewer-identity.d.ts +5 -0
- package/dist/mobile-remote/viewer-identity.js +5 -0
- package/dist/serve/cli.d.ts +15 -0
- package/dist/serve/cli.js +115 -0
- package/dist/serve/headless-server.d.ts +40 -0
- package/dist/serve/headless-server.js +276 -0
- package/dist/sessions-service.d.ts +73 -0
- package/dist/sessions-service.js +303 -0
- package/dist/static-files.d.ts +1 -0
- package/dist/static-files.js +25 -0
- package/dist/worker-bridge-core.d.ts +154 -0
- package/dist/worker-bridge-core.js +315 -0
- package/package.json +63 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// packages/server/src/worker-bridge-core.ts
|
|
2
|
+
//
|
|
3
|
+
// WorkerBridgeCore — transport-agnostic driver for a per-user agent worker
|
|
4
|
+
// subprocess speaking line-delimited JSON-RPC over stdio.
|
|
5
|
+
//
|
|
6
|
+
// This is the Electron-free half of the old AgentBridge, split out so other
|
|
7
|
+
// hosts (e.g. packages/server driving per-user stdio workers) can reuse it.
|
|
8
|
+
// It owns:
|
|
9
|
+
// - child process spawn / respawn (one worker at a time, spawned on demand)
|
|
10
|
+
// - stdout line framing (readline split) + listener dispatch
|
|
11
|
+
// - request/response correlation (id → resolver, with timeout / consume /
|
|
12
|
+
// settle-on-exit semantics)
|
|
13
|
+
// - inbound line injection (`injectWorkerMessage`) with a pluggable
|
|
14
|
+
// `prepareInbound` hook for host-specific rewriting (e.g. agent/run trust)
|
|
15
|
+
// - worker generation counter (bumped on every successful spawn)
|
|
16
|
+
// - pending outbox flushed once the worker's stdin exists
|
|
17
|
+
// - crash accounting (N crashes per rolling window ⇒ "gave up")
|
|
18
|
+
// - graceful shutdown (SIGTERM)
|
|
19
|
+
//
|
|
20
|
+
// It must have ZERO electron imports. All host semantics (renderer fan-out,
|
|
21
|
+
// pet projection, browser/credential intercepts, …) live in the caller via
|
|
22
|
+
// the constructor callbacks and `subscribeLines`.
|
|
23
|
+
import { spawn } from "node:child_process";
|
|
24
|
+
import { createInterface } from "node:readline";
|
|
25
|
+
const DEFAULT_RESTART_WINDOW_MS = 60_000;
|
|
26
|
+
const DEFAULT_RESTART_LIMIT = 3;
|
|
27
|
+
/** Truncate a wire line for logs. */
|
|
28
|
+
export function previewLine(s, max = 200) {
|
|
29
|
+
return s.length > max ? s.slice(0, max) + `…(+${s.length - max} more)` : s;
|
|
30
|
+
}
|
|
31
|
+
export class WorkerBridgeCore {
|
|
32
|
+
opts;
|
|
33
|
+
child = null;
|
|
34
|
+
/** Pending lines that arrived before the worker's stdin existed. */
|
|
35
|
+
outbox = [];
|
|
36
|
+
restartCount = 0;
|
|
37
|
+
restartWindowStart = Date.now();
|
|
38
|
+
generation = 0;
|
|
39
|
+
lineListeners = new Set();
|
|
40
|
+
pendingRequests = new Map();
|
|
41
|
+
restartWindowMs;
|
|
42
|
+
restartLimit;
|
|
43
|
+
constructor(opts) {
|
|
44
|
+
this.opts = opts;
|
|
45
|
+
this.restartWindowMs = opts.restartWindowMs ?? DEFAULT_RESTART_WINDOW_MS;
|
|
46
|
+
this.restartLimit = opts.restartLimit ?? DEFAULT_RESTART_LIMIT;
|
|
47
|
+
}
|
|
48
|
+
log(event, data) {
|
|
49
|
+
this.opts.log?.(event, data);
|
|
50
|
+
}
|
|
51
|
+
/** Current worker generation (0 until the first successful spawn). */
|
|
52
|
+
workerGeneration() {
|
|
53
|
+
return this.generation;
|
|
54
|
+
}
|
|
55
|
+
hasChild() {
|
|
56
|
+
return this.child !== null;
|
|
57
|
+
}
|
|
58
|
+
/** stdin exists and is not destroyed — a line write won't be dropped. */
|
|
59
|
+
canSend() {
|
|
60
|
+
return !!this.child?.stdin && !this.child.stdin.destroyed;
|
|
61
|
+
}
|
|
62
|
+
hasLiveWorker() {
|
|
63
|
+
return !!this.child?.stdin?.writable && !this.child.stdin.destroyed;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Spawn the worker if none is alive. `cwd` is the working directory the
|
|
67
|
+
* Engine will use (i.e. the repo root). Idempotent if a child is alive.
|
|
68
|
+
*/
|
|
69
|
+
ensureWorker(requestedCwd) {
|
|
70
|
+
if (this.child)
|
|
71
|
+
return;
|
|
72
|
+
const workerCwd = requestedCwd ?? this.opts.fallbackCwd?.() ?? process.cwd();
|
|
73
|
+
this.log("spawn.start", {
|
|
74
|
+
cwd: workerCwd,
|
|
75
|
+
requestedCwd,
|
|
76
|
+
restartCount: this.restartCount,
|
|
77
|
+
});
|
|
78
|
+
let child;
|
|
79
|
+
try {
|
|
80
|
+
child = spawn(this.opts.execPath ?? process.execPath, [this.opts.entryPath], {
|
|
81
|
+
cwd: workerCwd,
|
|
82
|
+
env: this.opts.buildEnv ? this.opts.buildEnv() : process.env,
|
|
83
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// spawn() can throw synchronously (e.g. invalid execPath). Don't let it
|
|
88
|
+
// bubble out of the caller uncaught — declare give-up so the host
|
|
89
|
+
// rejects the pending run instead of hanging.
|
|
90
|
+
this.log("spawn.throw", { error: String(e) });
|
|
91
|
+
this.child = null;
|
|
92
|
+
this.opts.onSpawnFailed?.();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.child = child;
|
|
96
|
+
this.log("spawn.ok", { pid: child.pid, cwd: workerCwd, requestedCwd });
|
|
97
|
+
if (!child.stdout || !child.stdin || !child.stderr) {
|
|
98
|
+
this.log("spawn.error", { reason: "stdio not piped" });
|
|
99
|
+
this.child = null;
|
|
100
|
+
this.opts.onSpawnFailed?.();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.generation += 1;
|
|
104
|
+
this.opts.onWorkerStarted?.({ generation: this.generation, pid: child.pid });
|
|
105
|
+
const rl = createInterface({ input: child.stdout });
|
|
106
|
+
rl.on("line", (line) => {
|
|
107
|
+
if (!line.trim())
|
|
108
|
+
return;
|
|
109
|
+
// Request/response correlation first: a consumed response never reaches
|
|
110
|
+
// the listeners (host-internal RPC); a non-consumed one is settled AND
|
|
111
|
+
// still dispatched so downstream mirrors see the identical stream.
|
|
112
|
+
if (this.settleMatchingRequest(line))
|
|
113
|
+
return;
|
|
114
|
+
for (const listener of [...this.lineListeners]) {
|
|
115
|
+
try {
|
|
116
|
+
listener(line);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// A listener must never break worker streaming.
|
|
120
|
+
this.log("line_listener.threw", { error: String(e) });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
child.stderr.on("data", (chunk) => {
|
|
125
|
+
this.opts.onStderr?.(chunk.toString());
|
|
126
|
+
});
|
|
127
|
+
// A failed spawn (ENOENT/EACCES/EAGAIN/process-limit) emits 'error' and
|
|
128
|
+
// NO 'exit'. Without this listener Node throws it as an uncaught
|
|
129
|
+
// exception, and neither exit path fires, so pending host runs hang
|
|
130
|
+
// forever. Treat a spawn error like a give-up crash.
|
|
131
|
+
child.on("error", (err) => {
|
|
132
|
+
this.log("child.error", { error: String(err) });
|
|
133
|
+
try {
|
|
134
|
+
rl.close();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
/* ignore */
|
|
138
|
+
}
|
|
139
|
+
this.child = null;
|
|
140
|
+
this.outbox = [];
|
|
141
|
+
this.settlePendingOnExit();
|
|
142
|
+
this.opts.onSpawnError?.(err);
|
|
143
|
+
});
|
|
144
|
+
child.on("exit", (code, signal) => {
|
|
145
|
+
// Close the readline interface bound to the dead child's stdout so it
|
|
146
|
+
// (and its "line" listener) doesn't leak across restarts.
|
|
147
|
+
rl.close();
|
|
148
|
+
this.log("child.exit", { code, signal, pid: child.pid });
|
|
149
|
+
this.child = null;
|
|
150
|
+
this.outbox = []; // any queued messages were for the dead child; drop
|
|
151
|
+
this.settlePendingOnExit();
|
|
152
|
+
if (code === 0 && signal === null) {
|
|
153
|
+
// Normal completion. Reset restart counter — clean exits don't count.
|
|
154
|
+
this.restartCount = 0;
|
|
155
|
+
this.opts.onExit?.({ code, signal, clean: true, gaveUp: false });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Real crash. Note it but DON'T pre-emptively respawn — the next run
|
|
159
|
+
// will trigger a fresh spawn anyway. We just decide whether to declare
|
|
160
|
+
// "gave up" so the host can surface it.
|
|
161
|
+
const gaveUp = this.shouldDeclareGaveUp();
|
|
162
|
+
if (gaveUp)
|
|
163
|
+
this.log("crash.gave_up", { restartCount: this.restartCount });
|
|
164
|
+
else
|
|
165
|
+
this.log("crash.tolerable", { restartCount: this.restartCount });
|
|
166
|
+
this.opts.onExit?.({ code, signal, clean: false, gaveUp });
|
|
167
|
+
});
|
|
168
|
+
// Flush queued lines now that stdin exists.
|
|
169
|
+
for (const queued of this.outbox) {
|
|
170
|
+
child.stdin.write(queued + "\n");
|
|
171
|
+
}
|
|
172
|
+
this.outbox = [];
|
|
173
|
+
}
|
|
174
|
+
/** Returns true after >= restartLimit crashes in the current window. */
|
|
175
|
+
shouldDeclareGaveUp() {
|
|
176
|
+
const now = Date.now();
|
|
177
|
+
if (now - this.restartWindowStart > this.restartWindowMs) {
|
|
178
|
+
this.restartWindowStart = now;
|
|
179
|
+
this.restartCount = 0;
|
|
180
|
+
}
|
|
181
|
+
this.restartCount++;
|
|
182
|
+
return this.restartCount > this.restartLimit;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* If `line` is a response to a pending request(), settle it. Returns true
|
|
186
|
+
* when the line was consumed (must NOT be dispatched to listeners).
|
|
187
|
+
*/
|
|
188
|
+
settleMatchingRequest(line) {
|
|
189
|
+
if (this.pendingRequests.size === 0)
|
|
190
|
+
return false;
|
|
191
|
+
let msg;
|
|
192
|
+
try {
|
|
193
|
+
msg = JSON.parse(line);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (msg.id === undefined || msg.id === null)
|
|
199
|
+
return false;
|
|
200
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
201
|
+
if (!pending)
|
|
202
|
+
return false;
|
|
203
|
+
pending.settle(msg.error
|
|
204
|
+
? { status: "error", error: msg.error }
|
|
205
|
+
: { status: "result", result: msg.result });
|
|
206
|
+
return pending.consume;
|
|
207
|
+
}
|
|
208
|
+
settlePendingOnExit() {
|
|
209
|
+
for (const pending of [...this.pendingRequests.values()]) {
|
|
210
|
+
if (pending.settleOnExit)
|
|
211
|
+
pending.settle({ status: "workerExit" });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Register an observer of every worker stdout line that wasn't consumed by
|
|
216
|
+
* a `consume: true` request. Returns an unsubscribe. A throwing listener
|
|
217
|
+
* never disrupts the stream.
|
|
218
|
+
*/
|
|
219
|
+
subscribeLines(listener) {
|
|
220
|
+
this.lineListeners.add(listener);
|
|
221
|
+
return () => this.lineListeners.delete(listener);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Write one JSON-RPC line to the worker's stdin (newline appended). Returns
|
|
225
|
+
* false when there is no live worker (caller decides drop/fallback). A
|
|
226
|
+
* stream write error propagates to the caller, matching a direct write.
|
|
227
|
+
*/
|
|
228
|
+
sendLine(line) {
|
|
229
|
+
const stdin = this.child?.stdin;
|
|
230
|
+
if (!stdin || stdin.destroyed)
|
|
231
|
+
return false;
|
|
232
|
+
stdin.write(line + "\n");
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Inject a JSON-RPC line into the worker exactly as a first-party front end
|
|
237
|
+
* would. The host's `prepareInbound` hook runs first (metadata rewriting +
|
|
238
|
+
* on-demand spawn side effects); the possibly-rewritten line is then
|
|
239
|
+
* written, or dropped with a log when no worker is alive — identical
|
|
240
|
+
* semantics for every front end (renderer, mobile remote, orchestrators).
|
|
241
|
+
*/
|
|
242
|
+
injectWorkerMessage(rawLine) {
|
|
243
|
+
const prep = this.opts.prepareInbound
|
|
244
|
+
? this.opts.prepareInbound(rawLine)
|
|
245
|
+
: { line: rawLine, method: undefined };
|
|
246
|
+
if (!this.canSend()) {
|
|
247
|
+
this.log("inject.dropped", {
|
|
248
|
+
reason: this.child ? "stdin destroyed" : "no child",
|
|
249
|
+
method: prep.method,
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
this.log("inject→worker", { method: prep.method, raw: previewLine(prep.line) });
|
|
254
|
+
this.sendLine(prep.line);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Send a correlated JSON-RPC request and await its response by id.
|
|
258
|
+
* `params` is omitted from the frame when undefined. Never rejects — the
|
|
259
|
+
* outcome union carries error/timeout/send-failure states for the caller
|
|
260
|
+
* to map onto its own semantics.
|
|
261
|
+
*/
|
|
262
|
+
request(method, params, options) {
|
|
263
|
+
const { id } = options;
|
|
264
|
+
return new Promise((resolve) => {
|
|
265
|
+
let settled = false;
|
|
266
|
+
const settle = (outcome) => {
|
|
267
|
+
if (settled)
|
|
268
|
+
return;
|
|
269
|
+
settled = true;
|
|
270
|
+
clearTimeout(timer);
|
|
271
|
+
this.pendingRequests.delete(id);
|
|
272
|
+
resolve(outcome);
|
|
273
|
+
};
|
|
274
|
+
const timer = setTimeout(() => settle({ status: "timeout" }), options.timeoutMs);
|
|
275
|
+
// unref so a long-lived correlation can never be the only thing keeping the
|
|
276
|
+
// process alive. Callers legitimately use very long timeouts (the Panel App
|
|
277
|
+
// fire-and-forget agent/run uses 24h); without this, a pending request of
|
|
278
|
+
// that length blocks a clean exit for a day. Settling still works normally
|
|
279
|
+
// while the process is running — unref only removes the event-loop hold.
|
|
280
|
+
timer.unref?.();
|
|
281
|
+
this.pendingRequests.set(id, {
|
|
282
|
+
consume: options.consume === true,
|
|
283
|
+
settleOnExit: options.settleOnExit === true,
|
|
284
|
+
settle,
|
|
285
|
+
});
|
|
286
|
+
const frame = JSON.stringify({
|
|
287
|
+
jsonrpc: "2.0",
|
|
288
|
+
id,
|
|
289
|
+
method,
|
|
290
|
+
...(params === undefined ? {} : { params }),
|
|
291
|
+
});
|
|
292
|
+
// Wake a lazily-unspawned worker before writing, so a spawn-triggering
|
|
293
|
+
// request (pet/IM-gateway agent/run) isn't dropped by sendLine and left
|
|
294
|
+
// to hang until timeout. Idempotent when a child is already alive.
|
|
295
|
+
if (options.ensureWorker)
|
|
296
|
+
this.ensureWorker(options.ensureWorkerCwd);
|
|
297
|
+
let sent = false;
|
|
298
|
+
let sendError;
|
|
299
|
+
try {
|
|
300
|
+
sent = this.sendLine(frame);
|
|
301
|
+
}
|
|
302
|
+
catch (e) {
|
|
303
|
+
sendError = e;
|
|
304
|
+
}
|
|
305
|
+
if (options.failFast && (!sent || sendError !== undefined)) {
|
|
306
|
+
settle({ status: "sendFailed", error: sendError });
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/** Graceful shutdown: SIGTERM the worker (no-op when none is alive). */
|
|
311
|
+
kill() {
|
|
312
|
+
this.log("kill", { pid: this.child?.pid });
|
|
313
|
+
this.child?.kill("SIGTERM");
|
|
314
|
+
}
|
|
315
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cjhyy/code-shell-server",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "CodeShell server transport layer — mobile remote HTTP/WS host, pairing, passcode gate, tunnel, rooms, uploads, and session/attachment services. Electron-free; precursor of the auth gateway.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./storage": {
|
|
14
|
+
"types": "./dist/index.storage.d.ts",
|
|
15
|
+
"import": "./dist/index.storage.js"
|
|
16
|
+
},
|
|
17
|
+
"./worker": {
|
|
18
|
+
"types": "./dist/index.worker.d.ts",
|
|
19
|
+
"import": "./dist/index.worker.js"
|
|
20
|
+
},
|
|
21
|
+
"./mobile-remote": {
|
|
22
|
+
"types": "./dist/index.mobile-remote.d.ts",
|
|
23
|
+
"import": "./dist/index.mobile-remote.js"
|
|
24
|
+
},
|
|
25
|
+
"./serve": {
|
|
26
|
+
"types": "./dist/index.serve.d.ts",
|
|
27
|
+
"import": "./dist/index.serve.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"code-shell-serve": "./dist/bin/code-shell-serve.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "bun run clean && tsc -p tsconfig.json",
|
|
38
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@cjhyy/code-shell-capability-coding": "0.8.0",
|
|
42
|
+
"@cjhyy/code-shell-core": "0.8.0",
|
|
43
|
+
"@cjhyy/code-shell-web": "0.8.0",
|
|
44
|
+
"@types/ws": "^8.18.1",
|
|
45
|
+
"ws": "^8.21.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20.10"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/cjhyy/codeshell.git",
|
|
53
|
+
"directory": "packages/server"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/cjhyy/codeshell/tree/main/packages/server#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/cjhyy/codeshell/issues"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT"
|
|
63
|
+
}
|