@co0ontty/wand 4.28.0 → 4.29.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/dist/build-info.json +3 -3
- package/dist/process-manager.d.ts +9 -0
- package/dist/process-manager.js +29 -8
- package/dist/server.js +37 -24
- package/dist/types.d.ts +3 -1
- package/dist/web-ui/content/scripts.js +38 -38
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/web-ui/provider-identity.js +2 -0
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-08-
|
|
4
|
-
"version": "4.
|
|
2
|
+
"commit": "ff3c41b2ee6aba11d741c8f27c891c25d2e51a72",
|
|
3
|
+
"builtAt": "2026-08-06T00:33:38.658Z",
|
|
4
|
+
"version": "4.29.0",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -51,6 +51,7 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
51
51
|
thinkingEffort?: SessionSnapshot["thinkingEffort"];
|
|
52
52
|
sessionSource?: SessionSource;
|
|
53
53
|
automationId?: string;
|
|
54
|
+
interactiveShell?: boolean;
|
|
54
55
|
}): SessionSnapshot;
|
|
55
56
|
list(): SessionSnapshot[];
|
|
56
57
|
/** Return lightweight snapshots for the session list (no output/messages). */
|
|
@@ -151,6 +152,14 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
151
152
|
*/
|
|
152
153
|
private handleBridgeEvent;
|
|
153
154
|
private mustGet;
|
|
155
|
+
/** Start a bare interactive login shell without a provider CLI. */
|
|
156
|
+
startShell(cwd: string | undefined, mode: ExecutionMode, opts?: {
|
|
157
|
+
worktreeEnabled?: boolean;
|
|
158
|
+
cols?: number;
|
|
159
|
+
rows?: number;
|
|
160
|
+
sessionSource?: SessionSource;
|
|
161
|
+
automationId?: string;
|
|
162
|
+
}): SessionSnapshot;
|
|
154
163
|
private buildShellArgs;
|
|
155
164
|
private shouldAutoApprovePermissions;
|
|
156
165
|
private processCommandForMode;
|
package/dist/process-manager.js
CHANGED
|
@@ -22,6 +22,8 @@ import { resolveSystemAiContext } from "./session-ai-context.js";
|
|
|
22
22
|
import { resolveSessionCwd } from "./session-cwd.js";
|
|
23
23
|
import { ProviderHistoryScanner, } from "./provider-history-scanner.js";
|
|
24
24
|
function resolveProviderFromCommand(command) {
|
|
25
|
+
if (/^(?:claude|npx\s+claude|[^\s]+\/claude)(?:\s|$)/.test(command.trim()))
|
|
26
|
+
return "claude";
|
|
25
27
|
if (/^codex\b/.test(command.trim()))
|
|
26
28
|
return "codex";
|
|
27
29
|
if (/^opencode\b/.test(command.trim()))
|
|
@@ -30,7 +32,7 @@ function resolveProviderFromCommand(command) {
|
|
|
30
32
|
return "grok";
|
|
31
33
|
if (/^pi\b/.test(command.trim()))
|
|
32
34
|
return "pi";
|
|
33
|
-
return /^qodercli\b/.test(command.trim()) ? "qoder" :
|
|
35
|
+
return /^qodercli\b/.test(command.trim()) ? "qoder" : undefined;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Tokenize the restricted shell-command subset accepted by the command
|
|
@@ -550,7 +552,9 @@ export class ProcessManager extends EventEmitter {
|
|
|
550
552
|
const isClaudeCmd = provider === "claude";
|
|
551
553
|
const isCodexCmd = provider === "codex";
|
|
552
554
|
const isOpenCodeCmd = provider === "opencode";
|
|
553
|
-
const resumeCommandSessionId =
|
|
555
|
+
const resumeCommandSessionId = provider
|
|
556
|
+
? getProviderCommandSessionId(provider, snapshot.command)
|
|
557
|
+
: null;
|
|
554
558
|
const orphanEndedAt = snapshot.status === "running" ? new Date().toISOString() : null;
|
|
555
559
|
const sessionIdFromHistory = isClaudeCmd
|
|
556
560
|
? recoverClaudeSessionIdFromHistory({
|
|
@@ -784,7 +788,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
784
788
|
start(command, cwd, mode, initialInput, opts) {
|
|
785
789
|
if (this.disposed)
|
|
786
790
|
throw new Error("ProcessManager has been disposed.");
|
|
787
|
-
|
|
791
|
+
if (!opts?.interactiveShell)
|
|
792
|
+
this.assertCommandAllowed(command);
|
|
788
793
|
const baseCwd = resolveSessionCwd(cwd, this.config.defaultCwd);
|
|
789
794
|
const id = opts?.reuseId || randomUUID();
|
|
790
795
|
// When a session is being resumed under the same id, capture its prior
|
|
@@ -815,7 +820,9 @@ export class ProcessManager extends EventEmitter {
|
|
|
815
820
|
? prepareSessionWorktree({ cwd: baseCwd, sessionId: id })
|
|
816
821
|
: null;
|
|
817
822
|
const resolvedCwd = worktreeSetup?.cwd ?? baseCwd;
|
|
818
|
-
const provider = opts?.
|
|
823
|
+
const provider = opts?.interactiveShell
|
|
824
|
+
? undefined
|
|
825
|
+
: opts?.provider ?? resolveProviderFromCommand(command);
|
|
819
826
|
const effectiveMode = provider === "codex" ? "full-access" : mode;
|
|
820
827
|
const isClaudeProvider = provider === "claude";
|
|
821
828
|
const selectedModel = opts?.model?.trim() || undefined;
|
|
@@ -823,8 +830,10 @@ export class ProcessManager extends EventEmitter {
|
|
|
823
830
|
let processedCommand = this.processCommandForMode(command, effectiveMode, provider, selectedModel, initialThinkingEffort);
|
|
824
831
|
const isCodexProvider = provider === "codex";
|
|
825
832
|
const isOpenCodeProvider = provider === "opencode";
|
|
826
|
-
const existingProviderSessionId =
|
|
827
|
-
|
|
833
|
+
const existingProviderSessionId = provider
|
|
834
|
+
? getProviderCommandSessionId(provider, processedCommand)
|
|
835
|
+
?? getProviderCommandSessionId(provider, command)
|
|
836
|
+
: null;
|
|
828
837
|
// Grok and Qoder accept caller-selected IDs for new conversations. Assigning
|
|
829
838
|
// one up front avoids depending on provider-specific TUI rendering to learn
|
|
830
839
|
// the durable ID later.
|
|
@@ -918,7 +927,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
918
927
|
this.providerHistory.invalidate(provider);
|
|
919
928
|
}
|
|
920
929
|
this.cleanupOldSessions();
|
|
921
|
-
const shellArgs = this.buildShellArgs(processedCommand);
|
|
930
|
+
const shellArgs = this.buildShellArgs(processedCommand, opts?.interactiveShell === true);
|
|
922
931
|
// Self-heal node-pty's spawn-helper +x bit before every spawn: a self-update
|
|
923
932
|
// can re-drop it after this server already ran its startup chmod, which would
|
|
924
933
|
// otherwise make every PTY launch throw "posix_spawnp failed." until restart.
|
|
@@ -2118,7 +2127,17 @@ export class ProcessManager extends EventEmitter {
|
|
|
2118
2127
|
}
|
|
2119
2128
|
return record;
|
|
2120
2129
|
}
|
|
2121
|
-
|
|
2130
|
+
/** Start a bare interactive login shell without a provider CLI. */
|
|
2131
|
+
startShell(cwd, mode, opts) {
|
|
2132
|
+
return this.start(this.config.shell, cwd, mode, undefined, {
|
|
2133
|
+
...opts,
|
|
2134
|
+
interactiveShell: true,
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
buildShellArgs(command, interactiveShell = false) {
|
|
2138
|
+
if (interactiveShell) {
|
|
2139
|
+
return os.platform() === "win32" ? [] : ["-l"];
|
|
2140
|
+
}
|
|
2122
2141
|
if (os.platform() === "win32") {
|
|
2123
2142
|
return ["/d", "/s", "/c", command];
|
|
2124
2143
|
}
|
|
@@ -2149,6 +2168,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
2149
2168
|
return false;
|
|
2150
2169
|
}
|
|
2151
2170
|
processCommandForMode(command, mode, provider, model, thinkingEffort) {
|
|
2171
|
+
if (!provider)
|
|
2172
|
+
return command;
|
|
2152
2173
|
if (provider === "codex") {
|
|
2153
2174
|
let result = command;
|
|
2154
2175
|
const trimmedModel = model?.trim();
|
package/dist/server.js
CHANGED
|
@@ -978,7 +978,8 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
978
978
|
// ── Session control ──
|
|
979
979
|
app.post("/api/commands", (req, res) => {
|
|
980
980
|
const body = req.body;
|
|
981
|
-
|
|
981
|
+
const interactiveShell = body.shell === true;
|
|
982
|
+
if (!interactiveShell && !body.command?.trim()) {
|
|
982
983
|
res.status(400).json({ error: "请输入要执行的命令。" });
|
|
983
984
|
return;
|
|
984
985
|
}
|
|
@@ -990,34 +991,46 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
990
991
|
try {
|
|
991
992
|
const origin = parseSessionCreationOrigin(body);
|
|
992
993
|
const rawModel = typeof body.model === "string" ? body.model.trim() : "";
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
994
|
+
const rawCommand = body.command?.trim() ?? "";
|
|
995
|
+
const provider = interactiveShell
|
|
996
|
+
? undefined
|
|
997
|
+
: body.provider === "codex" || /^codex\b/.test(rawCommand)
|
|
998
|
+
? "codex"
|
|
999
|
+
: body.provider === "opencode" || /^opencode\b/.test(rawCommand)
|
|
1000
|
+
? "opencode"
|
|
1001
|
+
: body.provider === "grok" || /^grok\b/.test(rawCommand)
|
|
1002
|
+
? "grok"
|
|
1003
|
+
: body.provider === "qoder" || /^qodercli\b/.test(rawCommand)
|
|
1004
|
+
? "qoder"
|
|
1005
|
+
: body.provider === "pi" || /^pi\b/.test(rawCommand)
|
|
1006
|
+
? "pi"
|
|
1007
|
+
: "claude";
|
|
1004
1008
|
// Older clients used the provider id as the PTY command. Qoder's executable
|
|
1005
1009
|
// is named qodercli, so keep those clients working while preserving custom commands.
|
|
1006
|
-
const command = provider === "qoder" &&
|
|
1010
|
+
const command = provider === "qoder" && rawCommand === "qoder"
|
|
1007
1011
|
? "qodercli"
|
|
1008
|
-
:
|
|
1009
|
-
const effectiveModel =
|
|
1012
|
+
: rawCommand;
|
|
1013
|
+
const effectiveModel = provider
|
|
1014
|
+
? rawModel || getDefaultModelForProvider(config, provider) || undefined
|
|
1015
|
+
: undefined;
|
|
1010
1016
|
const reqCols = typeof body.cols === "number" && Number.isFinite(body.cols) ? body.cols : undefined;
|
|
1011
1017
|
const reqRows = typeof body.rows === "number" && Number.isFinite(body.rows) ? body.rows : undefined;
|
|
1012
|
-
const snapshot =
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1018
|
+
const snapshot = interactiveShell
|
|
1019
|
+
? processes.startShell(body.cwd, body.mode ?? "default", {
|
|
1020
|
+
worktreeEnabled: body.worktreeEnabled === true,
|
|
1021
|
+
cols: reqCols,
|
|
1022
|
+
rows: reqRows,
|
|
1023
|
+
...origin,
|
|
1024
|
+
})
|
|
1025
|
+
: processes.start(command, body.cwd, body.mode ?? config.defaultMode, initialInput || undefined, {
|
|
1026
|
+
worktreeEnabled: body.worktreeEnabled === true,
|
|
1027
|
+
provider,
|
|
1028
|
+
model: effectiveModel,
|
|
1029
|
+
cols: reqCols,
|
|
1030
|
+
rows: reqRows,
|
|
1031
|
+
thinkingEffort: body.thinkingEffort ?? config.defaultThinkingEffort,
|
|
1032
|
+
...origin,
|
|
1033
|
+
});
|
|
1021
1034
|
recordRecentPath(storage, snapshot.cwd);
|
|
1022
1035
|
res.status(201).json(snapshot);
|
|
1023
1036
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -308,7 +308,9 @@ export interface PushResult {
|
|
|
308
308
|
error?: string;
|
|
309
309
|
}
|
|
310
310
|
export interface CommandRequest {
|
|
311
|
-
command
|
|
311
|
+
command?: string;
|
|
312
|
+
/** Start the configured login shell without launching a provider CLI. */
|
|
313
|
+
shell?: boolean;
|
|
312
314
|
provider?: SessionProvider;
|
|
313
315
|
cwd?: string;
|
|
314
316
|
mode?: ExecutionMode;
|