mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-29 21:19:14 +00:00
Compare commits
1 Commits
feat/javas
...
mingholy/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f970c9987 |
@@ -1,24 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
|
||||
[*.java]
|
||||
ij_java_doc_align_exception_comments = false
|
||||
ij_java_doc_align_param_comments = false
|
||||
|
||||
[*.{yaml, yml, sh, ps1}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{md, mkd, markdown}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{**/res/**.xml, **/AndroidManifest.xml}]
|
||||
ij_continuation_indent_size = 4
|
||||
13
packages/sdk-java/.gitignore
vendored
13
packages/sdk-java/.gitignore
vendored
@@ -1,13 +0,0 @@
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Maven
|
||||
log/
|
||||
target/
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<module name="FileTabCharacter" />
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\r" />
|
||||
<property name="message" value="Line contains carriage return" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value=" \n" />
|
||||
<property name="message" value="Line has trailing whitespace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\n" />
|
||||
<property name="message" value="Multiple consecutive blank lines" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\Z" />
|
||||
<property name="message" value="Blank line before end of file" />
|
||||
</module>
|
||||
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\{\n\n" />
|
||||
<property name="message" value="Blank line after opening brace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\s*\}" />
|
||||
<property name="message" value="Blank line before closing brace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="->\s*\{\s+\}" />
|
||||
<property name="message" value="Whitespace inside empty lambda body" />
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder" />
|
||||
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text" />
|
||||
<property name="tokens" value="
|
||||
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF,
|
||||
LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, INSTANCE_INIT, STATIC_INIT" />
|
||||
</module>
|
||||
<module name="EmptyStatement" />
|
||||
<module name="EmptyForInitializerPad" />
|
||||
<module name="MethodParamPad">
|
||||
<property name="allowLineBreaks" value="true" />
|
||||
<property name="option" value="nospace" />
|
||||
</module>
|
||||
<module name="ParenPad" />
|
||||
<module name="TypecastParenPad" />
|
||||
<module name="NeedBraces" />
|
||||
<module name="LeftCurly">
|
||||
<property name="option" value="eol" />
|
||||
<property name="tokens" value="
|
||||
LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR,
|
||||
LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE" />
|
||||
</module>
|
||||
<module name="GenericWhitespace" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="NoWhitespaceAfter" />
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<module name="SingleSpaceSeparator" />
|
||||
<module name="Indentation">
|
||||
<property name="throwsIndent" value="8" />
|
||||
<property name="lineWrappingIndentation" value="8" />
|
||||
</module>
|
||||
|
||||
<module name="UpperEll" />
|
||||
<module name="DefaultComesLast" />
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="ModifierOrder" />
|
||||
<module name="OneStatementPerLine" />
|
||||
<module name="StringLiteralEquality" />
|
||||
<module name="MutableException" />
|
||||
<module name="EqualsHashCode" />
|
||||
<module name="ExplicitInitialization" />
|
||||
<module name="OneTopLevelClass" />
|
||||
|
||||
<module name="MemberName" />
|
||||
<module name="PackageName" />
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="^[A-Z][0-9]?$" />
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="^[A-Z][0-9]?$" />
|
||||
</module>
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="trailingArrayComma" value="ignore" />
|
||||
</module>
|
||||
|
||||
<module name="RedundantImport" />
|
||||
<module name="UnusedImports" />
|
||||
<!-- <module name="ImportOrder">-->
|
||||
<!-- <property name="groups" value="*,javax,java" />-->
|
||||
<!-- <property name="separated" value="true" />-->
|
||||
<!-- <property name="option" value="bottom" />-->
|
||||
<!-- <property name="sortStaticImportsAlphabetically" value="true" />-->
|
||||
<!-- </module>-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true" />
|
||||
<property name="allowEmptyMethods" value="true" />
|
||||
<property name="allowEmptyLambdas" value="true" />
|
||||
<property name="ignoreEnhancedForColon" value="false" />
|
||||
<property name="tokens" value="
|
||||
ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN,
|
||||
BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAND,
|
||||
LAMBDA, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE,
|
||||
LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL,
|
||||
PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN,
|
||||
STAR, STAR_ASSIGN, TYPE_EXTENSION_AND" />
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter" />
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<property name="tokens" value="DOT" />
|
||||
<property name="allowLineBreaks" value="false" />
|
||||
</module>
|
||||
|
||||
<module name="MissingOverride"/>
|
||||
</module>
|
||||
</module>
|
||||
@@ -1,111 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>qwencode-sdk-java</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.0.1</version>
|
||||
<name>qwencode-sdk-java</name>
|
||||
<url>https://maven.apache.org</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>A business-friendly OSS license</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>https://github.com/QwenLM/qwen-code</url>
|
||||
<connection>scm:git:https://github.com/QwenLM/qwen-code.git</connection>
|
||||
</scm>
|
||||
<properties>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<checkstyle-maven-plugin.version>3.6.0</checkstyle-maven-plugin.version>
|
||||
<junit5.version>5.14.1</junit5.version>
|
||||
<logback-classic.version>1.3.16</logback-classic.version>
|
||||
<fastjson2.version>2.0.60</fastjson2.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${junit5.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback-classic.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.20.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${checkstyle-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<organization>
|
||||
<name>Alibaba Group</name>
|
||||
<url>https://github.com/alibaba</url>
|
||||
</organization>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>skyfire</id>
|
||||
<name>skyfire</name>
|
||||
<email>gengwei.gw(at)alibaba-inc.com</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
<role>Designer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/gwinthis</url>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>central</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
public class Options extends TransportOptions {
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.message.Message;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
|
||||
|
||||
public class QwenCli {
|
||||
public static List<Message> query(String prompt) {
|
||||
Transport transport;
|
||||
try {
|
||||
transport = new ProcessTransport();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("initialized ProcessTransport error!", e);
|
||||
}
|
||||
|
||||
Session session;
|
||||
try {
|
||||
session = new Session(transport);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("initialized Session error!", e);
|
||||
}
|
||||
|
||||
final List<Message> response = new ArrayList<>();
|
||||
try {
|
||||
session.sendPrompt(prompt, new SessionEventSimpleConsumers() {
|
||||
@Override
|
||||
public void onSystemMessage(SDKSystemMessage systemMessage) {
|
||||
response.add(systemMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
|
||||
response.add(assistantMessage);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("sendPrompt error!", e);
|
||||
}
|
||||
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("close Session error!", e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class CLIPermissionDenial {
|
||||
@JSONField(name = "tool_name")
|
||||
private String toolName;
|
||||
|
||||
@JSONField(name = "tool_use_id")
|
||||
private String toolUseId;
|
||||
|
||||
@JSONField(name = "tool_input")
|
||||
private Object toolInput;
|
||||
|
||||
public String getToolName() {
|
||||
return toolName;
|
||||
}
|
||||
|
||||
public void setToolName(String toolName) {
|
||||
this.toolName = toolName;
|
||||
}
|
||||
|
||||
public String getToolUseId() {
|
||||
return toolUseId;
|
||||
}
|
||||
|
||||
public void setToolUseId(String toolUseId) {
|
||||
this.toolUseId = toolUseId;
|
||||
}
|
||||
|
||||
public Object getToolInput() {
|
||||
return toolInput;
|
||||
}
|
||||
|
||||
public void setToolInput(Object toolInput) {
|
||||
this.toolInput = toolInput;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class Capabilities {
|
||||
@JSONField(name = "can_handle_can_use_tool")
|
||||
boolean canHandleCanUseTool;
|
||||
|
||||
@JSONField(name = "can_handle_hook_callback")
|
||||
boolean canHandleHookCallback;
|
||||
|
||||
@JSONField(name = "can_set_permission_mode")
|
||||
boolean canSetPermissionMode;
|
||||
|
||||
@JSONField(name = "can_set_model")
|
||||
boolean canSetModel;
|
||||
|
||||
@JSONField(name = "can_handle_mcp_message")
|
||||
boolean canHandleMcpMessage;
|
||||
|
||||
public boolean isCanHandleCanUseTool() {
|
||||
return canHandleCanUseTool;
|
||||
}
|
||||
|
||||
public void setCanHandleCanUseTool(boolean canHandleCanUseTool) {
|
||||
this.canHandleCanUseTool = canHandleCanUseTool;
|
||||
}
|
||||
|
||||
public boolean isCanHandleHookCallback() {
|
||||
return canHandleHookCallback;
|
||||
}
|
||||
|
||||
public void setCanHandleHookCallback(boolean canHandleHookCallback) {
|
||||
this.canHandleHookCallback = canHandleHookCallback;
|
||||
}
|
||||
|
||||
public boolean isCanSetPermissionMode() {
|
||||
return canSetPermissionMode;
|
||||
}
|
||||
|
||||
public void setCanSetPermissionMode(boolean canSetPermissionMode) {
|
||||
this.canSetPermissionMode = canSetPermissionMode;
|
||||
}
|
||||
|
||||
public boolean isCanSetModel() {
|
||||
return canSetModel;
|
||||
}
|
||||
|
||||
public void setCanSetModel(boolean canSetModel) {
|
||||
this.canSetModel = canSetModel;
|
||||
}
|
||||
|
||||
public boolean isCanHandleMcpMessage() {
|
||||
return canHandleMcpMessage;
|
||||
}
|
||||
|
||||
public void setCanHandleMcpMessage(boolean canHandleMcpMessage) {
|
||||
this.canHandleMcpMessage = canHandleMcpMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class ExtendedUsage extends Usage {
|
||||
@JSONField(name = "server_tool_use")
|
||||
private ServerToolUse serverToolUse;
|
||||
|
||||
@JSONField(name = "service_tier")
|
||||
private String serviceTier;
|
||||
|
||||
@JSONField(name = "cache_creation")
|
||||
private CacheCreation cacheCreation;
|
||||
|
||||
public ServerToolUse getServerToolUse() {
|
||||
return serverToolUse;
|
||||
}
|
||||
|
||||
public void setServerToolUse(ServerToolUse serverToolUse) {
|
||||
this.serverToolUse = serverToolUse;
|
||||
}
|
||||
|
||||
public String getServiceTier() {
|
||||
return serviceTier;
|
||||
}
|
||||
|
||||
public void setServiceTier(String serviceTier) {
|
||||
this.serviceTier = serviceTier;
|
||||
}
|
||||
|
||||
public CacheCreation getCacheCreation() {
|
||||
return cacheCreation;
|
||||
}
|
||||
|
||||
public void setCacheCreation(CacheCreation cacheCreation) {
|
||||
this.cacheCreation = cacheCreation;
|
||||
}
|
||||
|
||||
public static class ServerToolUse {
|
||||
@JSONField(name = "web_search_requests")
|
||||
private int webSearchRequests;
|
||||
}
|
||||
|
||||
public static class CacheCreation {
|
||||
@JSONField(name = "ephemeral_1h_input_tokens")
|
||||
private int ephemeral1hInputTokens;
|
||||
|
||||
@JSONField(name = "ephemeral_5m_input_tokens")
|
||||
private int ephemeral5mInputTokens;
|
||||
|
||||
public int getEphemeral1hInputTokens() {
|
||||
return ephemeral1hInputTokens;
|
||||
}
|
||||
|
||||
public void setEphemeral1hInputTokens(int ephemeral1hInputTokens) {
|
||||
this.ephemeral1hInputTokens = ephemeral1hInputTokens;
|
||||
}
|
||||
|
||||
public int getEphemeral5mInputTokens() {
|
||||
return ephemeral5mInputTokens;
|
||||
}
|
||||
|
||||
public void setEphemeral5mInputTokens(int ephemeral5mInputTokens) {
|
||||
this.ephemeral5mInputTokens = ephemeral5mInputTokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
public class InitializeConfig {
|
||||
String hooks;
|
||||
String sdkMcpServers;
|
||||
String mcpServers;
|
||||
String agents;
|
||||
|
||||
public String getHooks() {
|
||||
return hooks;
|
||||
}
|
||||
|
||||
public void setHooks(String hooks) {
|
||||
this.hooks = hooks;
|
||||
}
|
||||
|
||||
public String getSdkMcpServers() {
|
||||
return sdkMcpServers;
|
||||
}
|
||||
|
||||
public void setSdkMcpServers(String sdkMcpServers) {
|
||||
this.sdkMcpServers = sdkMcpServers;
|
||||
}
|
||||
|
||||
public String getMcpServers() {
|
||||
return mcpServers;
|
||||
}
|
||||
|
||||
public void setMcpServers(String mcpServers) {
|
||||
this.mcpServers = mcpServers;
|
||||
}
|
||||
|
||||
public String getAgents() {
|
||||
return agents;
|
||||
}
|
||||
|
||||
public void setAgents(String agents) {
|
||||
this.agents = agents;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
public class ModelUsage {
|
||||
private int inputTokens;
|
||||
private int outputTokens;
|
||||
private int cacheReadInputTokens;
|
||||
private int cacheCreationInputTokens;
|
||||
private int webSearchRequests;
|
||||
private int contextWindow;
|
||||
|
||||
public int getInputTokens() {
|
||||
return inputTokens;
|
||||
}
|
||||
|
||||
public void setInputTokens(int inputTokens) {
|
||||
this.inputTokens = inputTokens;
|
||||
}
|
||||
|
||||
public int getOutputTokens() {
|
||||
return outputTokens;
|
||||
}
|
||||
|
||||
public void setOutputTokens(int outputTokens) {
|
||||
this.outputTokens = outputTokens;
|
||||
}
|
||||
|
||||
public int getCacheReadInputTokens() {
|
||||
return cacheReadInputTokens;
|
||||
}
|
||||
|
||||
public void setCacheReadInputTokens(int cacheReadInputTokens) {
|
||||
this.cacheReadInputTokens = cacheReadInputTokens;
|
||||
}
|
||||
|
||||
public int getCacheCreationInputTokens() {
|
||||
return cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
public void setCacheCreationInputTokens(int cacheCreationInputTokens) {
|
||||
this.cacheCreationInputTokens = cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
public int getWebSearchRequests() {
|
||||
return webSearchRequests;
|
||||
}
|
||||
|
||||
public void setWebSearchRequests(int webSearchRequests) {
|
||||
this.webSearchRequests = webSearchRequests;
|
||||
}
|
||||
|
||||
public int getContextWindow() {
|
||||
return contextWindow;
|
||||
}
|
||||
|
||||
public void setContextWindow(int contextWindow) {
|
||||
this.contextWindow = contextWindow;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class Usage {
|
||||
@JSONField(name = "input_tokens")
|
||||
private Integer inputTokens;
|
||||
@JSONField(name = "output_tokens")
|
||||
private Integer outputTokens;
|
||||
@JSONField(name = "cache_creation_input_tokens")
|
||||
private Integer cacheCreationInputTokens;
|
||||
@JSONField(name = "cache_read_input_tokens")
|
||||
private Integer cacheReadInputTokens;
|
||||
@JSONField(name = "total_tokens")
|
||||
private Integer totalTokens;
|
||||
|
||||
public Integer getInputTokens() {
|
||||
return inputTokens;
|
||||
}
|
||||
|
||||
public void setInputTokens(Integer inputTokens) {
|
||||
this.inputTokens = inputTokens;
|
||||
}
|
||||
|
||||
public Integer getOutputTokens() {
|
||||
return outputTokens;
|
||||
}
|
||||
|
||||
public void setOutputTokens(Integer outputTokens) {
|
||||
this.outputTokens = outputTokens;
|
||||
}
|
||||
|
||||
public Integer getCacheCreationInputTokens() {
|
||||
return cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
public void setCacheCreationInputTokens(Integer cacheCreationInputTokens) {
|
||||
this.cacheCreationInputTokens = cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
public Integer getCacheReadInputTokens() {
|
||||
return cacheReadInputTokens;
|
||||
}
|
||||
|
||||
public void setCacheReadInputTokens(Integer cacheReadInputTokens) {
|
||||
this.cacheReadInputTokens = cacheReadInputTokens;
|
||||
}
|
||||
|
||||
public Integer getTotalTokens() {
|
||||
return totalTokens;
|
||||
}
|
||||
|
||||
public void setTotalTokens(Integer totalTokens) {
|
||||
this.totalTokens = totalTokens;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
public interface Message {
|
||||
String getType();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(alphabetic = false, typeKey = "type", typeName = "MessageBase")
|
||||
public class MessageBase implements Message{
|
||||
protected String type;
|
||||
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.CLIPermissionDenial;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.ExtendedUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Usage;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "result")
|
||||
public class SDKResultMessage extends MessageBase {
|
||||
private String subtype; // 'error_max_turns' | 'error_during_execution'
|
||||
private String uuid;
|
||||
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
|
||||
@JSONField(name = "is_error")
|
||||
private boolean isError = true;
|
||||
|
||||
@JSONField(name = "duration_ms")
|
||||
private Long durationMs;
|
||||
|
||||
@JSONField(name = "duration_api_ms")
|
||||
private Long durationApiMs;
|
||||
|
||||
@JSONField(name = "num_turns")
|
||||
private Integer numTurns;
|
||||
private ExtendedUsage usage;
|
||||
private Map<String, Usage> modelUsage;
|
||||
|
||||
@JSONField(name = "permission_denials")
|
||||
private List<CLIPermissionDenial> permissionDenials;
|
||||
private Error error;
|
||||
|
||||
public SDKResultMessage() {
|
||||
super();
|
||||
this.type = "result";
|
||||
}
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
public void setError(boolean error) {
|
||||
isError = error;
|
||||
}
|
||||
|
||||
public Long getDurationMs() {
|
||||
return durationMs;
|
||||
}
|
||||
|
||||
public void setDurationMs(Long durationMs) {
|
||||
this.durationMs = durationMs;
|
||||
}
|
||||
|
||||
public Long getDurationApiMs() {
|
||||
return durationApiMs;
|
||||
}
|
||||
|
||||
public void setDurationApiMs(Long durationApiMs) {
|
||||
this.durationApiMs = durationApiMs;
|
||||
}
|
||||
|
||||
public Integer getNumTurns() {
|
||||
return numTurns;
|
||||
}
|
||||
|
||||
public void setNumTurns(Integer numTurns) {
|
||||
this.numTurns = numTurns;
|
||||
}
|
||||
|
||||
public ExtendedUsage getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
public void setUsage(ExtendedUsage usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
public Map<String, Usage> getModelUsage() {
|
||||
return modelUsage;
|
||||
}
|
||||
|
||||
public void setModelUsage(Map<String, Usage> modelUsage) {
|
||||
this.modelUsage = modelUsage;
|
||||
}
|
||||
|
||||
public List<CLIPermissionDenial> getPermissionDenials() {
|
||||
return permissionDenials;
|
||||
}
|
||||
|
||||
public void setPermissionDenials(List<CLIPermissionDenial> permissionDenials) {
|
||||
this.permissionDenials = permissionDenials;
|
||||
}
|
||||
|
||||
public Error getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(Error error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public static class Error {
|
||||
private String type;
|
||||
private String message;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "system")
|
||||
public class SDKSystemMessage extends MessageBase {
|
||||
private String subtype;
|
||||
private String uuid;
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
private Object data;
|
||||
private String cwd;
|
||||
private List<String> tools;
|
||||
@JSONField(name = "mcp_servers")
|
||||
private List<McpServer> mcpServers;
|
||||
private String model;
|
||||
@JSONField(name = "permission_mode")
|
||||
private String permissionMode;
|
||||
@JSONField(name = "slash_commands")
|
||||
private List<String> slashCommands;
|
||||
@JSONField(name = "qwen_code_version")
|
||||
private String qwenCodeVersion;
|
||||
@JSONField(name = "output_style")
|
||||
private String outputStyle;
|
||||
private List<String> agents;
|
||||
private List<String> skills;
|
||||
private Map<String, Object> capabilities;
|
||||
@JSONField(name = "compact_metadata")
|
||||
private CompactMetadata compactMetadata;
|
||||
|
||||
public SDKSystemMessage() {
|
||||
super();
|
||||
this.type = "system";
|
||||
}
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getCwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
public void setCwd(String cwd) {
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
public List<String> getTools() {
|
||||
return tools;
|
||||
}
|
||||
|
||||
public void setTools(List<String> tools) {
|
||||
this.tools = tools;
|
||||
}
|
||||
|
||||
public List<McpServer> getMcpServers() {
|
||||
return mcpServers;
|
||||
}
|
||||
|
||||
public void setMcpServers(List<McpServer> mcpServers) {
|
||||
this.mcpServers = mcpServers;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getPermissionMode() {
|
||||
return permissionMode;
|
||||
}
|
||||
|
||||
public void setPermissionMode(String permissionMode) {
|
||||
this.permissionMode = permissionMode;
|
||||
}
|
||||
|
||||
public List<String> getSlashCommands() {
|
||||
return slashCommands;
|
||||
}
|
||||
|
||||
public void setSlashCommands(List<String> slashCommands) {
|
||||
this.slashCommands = slashCommands;
|
||||
}
|
||||
|
||||
public String getQwenCodeVersion() {
|
||||
return qwenCodeVersion;
|
||||
}
|
||||
|
||||
public void setQwenCodeVersion(String qwenCodeVersion) {
|
||||
this.qwenCodeVersion = qwenCodeVersion;
|
||||
}
|
||||
|
||||
public String getOutputStyle() {
|
||||
return outputStyle;
|
||||
}
|
||||
|
||||
public void setOutputStyle(String outputStyle) {
|
||||
this.outputStyle = outputStyle;
|
||||
}
|
||||
|
||||
public List<String> getAgents() {
|
||||
return agents;
|
||||
}
|
||||
|
||||
public void setAgents(List<String> agents) {
|
||||
this.agents = agents;
|
||||
}
|
||||
|
||||
public List<String> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
public void setSkills(List<String> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public void setCapabilities(Map<String, Object> capabilities) {
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
public CompactMetadata getCompactMetadata() {
|
||||
return compactMetadata;
|
||||
}
|
||||
|
||||
public void setCompactMetadata(CompactMetadata compactMetadata) {
|
||||
this.compactMetadata = compactMetadata;
|
||||
}
|
||||
|
||||
public static class McpServer {
|
||||
private String name;
|
||||
private String status;
|
||||
|
||||
// Getters and setters
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompactMetadata {
|
||||
private String trigger;
|
||||
|
||||
@JSONField(name = "pre_tokens")
|
||||
private Integer preTokens;
|
||||
|
||||
// Getters and setters
|
||||
public String getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
public void setTrigger(String trigger) {
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
public Integer getPreTokens() {
|
||||
return preTokens;
|
||||
}
|
||||
|
||||
public void setPreTokens(Integer preTokens) {
|
||||
this.preTokens = preTokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "user")
|
||||
public class SDKUserMessage extends MessageBase {
|
||||
private String uuid;
|
||||
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
private final APIUserMessage message = new APIUserMessage();
|
||||
|
||||
@JSONField(name = "parent_tool_use_id")
|
||||
private String parentToolUseId;
|
||||
private Map<String, String> options;
|
||||
|
||||
public SDKUserMessage() {
|
||||
super();
|
||||
this.setType("user");
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public SDKUserMessage setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SDKUserMessage setContent(String content) {
|
||||
message.setContent(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return message.getContent();
|
||||
}
|
||||
|
||||
public String getParentToolUseId() {
|
||||
return parentToolUseId;
|
||||
}
|
||||
|
||||
public SDKUserMessage setParentToolUseId(String parentToolUseId) {
|
||||
this.parentToolUseId = parentToolUseId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public SDKUserMessage setOptions(Map<String, String> options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class APIUserMessage {
|
||||
private String role = "user";
|
||||
private String content;
|
||||
|
||||
// Getters and Setters
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Usage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.block.ContentBlock;
|
||||
|
||||
public class APIAssistantMessage {
|
||||
private String id;
|
||||
private String type = "message";
|
||||
private String role = "assistant";
|
||||
private String model;
|
||||
private List<ContentBlock> content;
|
||||
|
||||
@JSONField(name = "stop_reason")
|
||||
private String stopReason;
|
||||
private Usage usage;
|
||||
|
||||
// Getters and setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getStopReason() {
|
||||
return stopReason;
|
||||
}
|
||||
|
||||
public void setStopReason(String stopReason) {
|
||||
this.stopReason = stopReason;
|
||||
}
|
||||
|
||||
public Usage getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
public void setUsage(Usage usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
public List<ContentBlock> getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(List<ContentBlock> content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "assistant")
|
||||
public class SDKAssistantMessage extends MessageBase {
|
||||
private String uuid;
|
||||
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
private APIAssistantMessage message;
|
||||
|
||||
@JSONField(name = "parent_tool_use_id")
|
||||
private String parentToolUseId;
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public APIAssistantMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(APIAssistantMessage message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getParentToolUseId() {
|
||||
return parentToolUseId;
|
||||
}
|
||||
|
||||
public void setParentToolUseId(String parentToolUseId) {
|
||||
this.parentToolUseId = parentToolUseId;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class Annotation {
|
||||
@JSONField(name = "type")
|
||||
private String type;
|
||||
|
||||
@JSONField(name = "value")
|
||||
private String value;
|
||||
|
||||
// Getters and setters
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "ContentBlock", seeAlso = { TextBlock.class, ToolResultBlock.class, ThinkingBlock.class, ToolUseBlock.class })
|
||||
public class ContentBlock {
|
||||
protected String type;
|
||||
protected List<Annotation> annotations;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public List<Annotation> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public void setAnnotations(List<Annotation> annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "text")
|
||||
public class TextBlock extends ContentBlock {
|
||||
private String text;
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "thinking")
|
||||
public class ThinkingBlock extends ContentBlock{
|
||||
private String thinking;
|
||||
private String signature;
|
||||
|
||||
public String getThinking() {
|
||||
return thinking;
|
||||
}
|
||||
|
||||
public void setThinking(String thinking) {
|
||||
this.thinking = thinking;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "tool_result")
|
||||
public class ToolResultBlock extends ContentBlock {
|
||||
@JSONField(name = "tool_use_id")
|
||||
private String toolUseId;
|
||||
|
||||
@JSONField(name = "content")
|
||||
private Object content; // Can be String or List<ContentBlock>
|
||||
|
||||
@JSONField(name = "is_error")
|
||||
private Boolean isError;
|
||||
|
||||
public String getToolUseId() {
|
||||
return toolUseId;
|
||||
}
|
||||
|
||||
public void setToolUseId(String toolUseId) {
|
||||
this.toolUseId = toolUseId;
|
||||
}
|
||||
|
||||
public Object getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(Object content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Boolean getIsError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
public void setIsError(Boolean isError) {
|
||||
this.isError = isError;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "tool_use")
|
||||
public class ToolUseBlock extends ContentBlock {
|
||||
private String id;
|
||||
private String name;
|
||||
private Map<String, Object> input;
|
||||
private List<Annotation> annotations;
|
||||
|
||||
// 构造函数
|
||||
public ToolUseBlock() {}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Map<String, Object> getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput(Map<String, Object> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public List<Annotation> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public void setAnnotations(List<Annotation> annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.InitializeConfig;
|
||||
|
||||
public class CLIControlInitializeRequest {
|
||||
String subtype = "initialize";
|
||||
|
||||
@JSONField(unwrapped = true)
|
||||
InitializeConfig initializeConfig = new InitializeConfig();
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public InitializeConfig getInitializeConfig() {
|
||||
return initializeConfig;
|
||||
}
|
||||
|
||||
public CLIControlInitializeRequest setInitializeConfig(InitializeConfig initializeConfig) {
|
||||
this.initializeConfig = initializeConfig;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
|
||||
|
||||
public class CLIControlInitializeResponse {
|
||||
String subtype = "initialize";
|
||||
Capabilities capabilities;
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public Capabilities getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public void setCapabilities(Capabilities capabilities) {
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "control_request")
|
||||
public class CLIControlRequest<R> extends MessageBase {
|
||||
@JSONField(name = "request_id")
|
||||
private String requestId = UUID.randomUUID().toString();
|
||||
|
||||
private R request;
|
||||
|
||||
public CLIControlRequest() {
|
||||
super();
|
||||
type = "control_request";
|
||||
}
|
||||
|
||||
public static <T> CLIControlRequest<T> create(T request) {
|
||||
CLIControlRequest<T> controlRequest = new CLIControlRequest<>();
|
||||
controlRequest.setRequest(request);
|
||||
return controlRequest;
|
||||
}
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public CLIControlRequest<R> setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public R getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public CLIControlRequest<R> setRequest(R request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
|
||||
@JSONType(typeKey = "type", typeName = "control_response")
|
||||
public class CLIControlResponse<R> extends MessageBase {
|
||||
private Response<R> response;
|
||||
|
||||
public CLIControlResponse() {
|
||||
super();
|
||||
this.type = "control_response";
|
||||
}
|
||||
|
||||
public Response<R> getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(Response<R> response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public static class Response<R> {
|
||||
@JSONField(name = "request_id")
|
||||
private String requestId;
|
||||
private String subtype;
|
||||
R response;
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public R getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(R response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,594 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
export interface Annotation {
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Usage {
|
||||
input_tokens: number;
|
||||
output_tokens: number;
|
||||
cache_creation_input_tokens?: number;
|
||||
cache_read_input_tokens?: number;
|
||||
total_tokens?: number;
|
||||
}
|
||||
|
||||
export interface ExtendedUsage extends Usage {
|
||||
server_tool_use?: {
|
||||
web_search_requests: number;
|
||||
};
|
||||
service_tier?: string;
|
||||
cache_creation?: {
|
||||
ephemeral_1h_input_tokens: number;
|
||||
ephemeral_5m_input_tokens: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ModelUsage {
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
cacheReadInputTokens: number;
|
||||
cacheCreationInputTokens: number;
|
||||
webSearchRequests: number;
|
||||
contextWindow: number;
|
||||
}
|
||||
|
||||
export interface CLIPermissionDenial {
|
||||
tool_name: string;
|
||||
tool_use_id: string;
|
||||
tool_input: unknown;
|
||||
}
|
||||
|
||||
export interface TextBlock {
|
||||
type: 'text';
|
||||
text: string;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ThinkingBlock {
|
||||
type: 'thinking';
|
||||
thinking: string;
|
||||
signature?: string;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ToolUseBlock {
|
||||
type: 'tool_use';
|
||||
id: string;
|
||||
name: string;
|
||||
input: unknown;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ToolResultBlock {
|
||||
type: 'tool_result';
|
||||
tool_use_id: string;
|
||||
content?: string | ContentBlock[];
|
||||
is_error?: boolean;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export type ContentBlock =
|
||||
| TextBlock
|
||||
| ThinkingBlock
|
||||
| ToolUseBlock
|
||||
| ToolResultBlock;
|
||||
|
||||
export interface APIUserMessage {
|
||||
role: 'user';
|
||||
content: string | ContentBlock[];
|
||||
}
|
||||
|
||||
export interface APIAssistantMessage {
|
||||
id: string;
|
||||
type: 'message';
|
||||
role: 'assistant';
|
||||
model: string;
|
||||
content: ContentBlock[];
|
||||
stop_reason?: string | null;
|
||||
usage: Usage;
|
||||
}
|
||||
|
||||
export interface SDKUserMessage {
|
||||
type: 'user';
|
||||
uuid?: string;
|
||||
session_id: string;
|
||||
message: APIUserMessage;
|
||||
parent_tool_use_id: string | null;
|
||||
options?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface SDKAssistantMessage {
|
||||
type: 'assistant';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
message: APIAssistantMessage;
|
||||
parent_tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export interface SDKSystemMessage {
|
||||
type: 'system';
|
||||
subtype: string;
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
data?: unknown;
|
||||
cwd?: string;
|
||||
tools?: string[];
|
||||
mcp_servers?: Array<{
|
||||
name: string;
|
||||
status: string;
|
||||
}>;
|
||||
model?: string;
|
||||
permission_mode?: string;
|
||||
slash_commands?: string[];
|
||||
qwen_code_version?: string;
|
||||
output_style?: string;
|
||||
agents?: string[];
|
||||
skills?: string[];
|
||||
capabilities?: Record<string, unknown>;
|
||||
compact_metadata?: {
|
||||
trigger: 'manual' | 'auto';
|
||||
pre_tokens: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SDKResultMessageSuccess {
|
||||
type: 'result';
|
||||
subtype: 'success';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
is_error: false;
|
||||
duration_ms: number;
|
||||
duration_api_ms: number;
|
||||
num_turns: number;
|
||||
result: string;
|
||||
usage: ExtendedUsage;
|
||||
modelUsage?: Record<string, ModelUsage>;
|
||||
permission_denials: CLIPermissionDenial[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface SDKResultMessageError {
|
||||
type: 'result';
|
||||
subtype: 'error_max_turns' | 'error_during_execution';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
is_error: true;
|
||||
duration_ms: number;
|
||||
duration_api_ms: number;
|
||||
num_turns: number;
|
||||
usage: ExtendedUsage;
|
||||
modelUsage?: Record<string, ModelUsage>;
|
||||
permission_denials: CLIPermissionDenial[];
|
||||
error?: {
|
||||
type?: string;
|
||||
message: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type SDKResultMessage = SDKResultMessageSuccess | SDKResultMessageError;
|
||||
|
||||
export interface MessageStartStreamEvent {
|
||||
type: 'message_start';
|
||||
message: {
|
||||
id: string;
|
||||
role: 'assistant';
|
||||
model: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ContentBlockStartEvent {
|
||||
type: 'content_block_start';
|
||||
index: number;
|
||||
content_block: ContentBlock;
|
||||
}
|
||||
|
||||
export type ContentBlockDelta =
|
||||
| {
|
||||
type: 'text_delta';
|
||||
text: string;
|
||||
}
|
||||
| {
|
||||
type: 'thinking_delta';
|
||||
thinking: string;
|
||||
}
|
||||
| {
|
||||
type: 'input_json_delta';
|
||||
partial_json: string;
|
||||
};
|
||||
|
||||
export interface ContentBlockDeltaEvent {
|
||||
type: 'content_block_delta';
|
||||
index: number;
|
||||
delta: ContentBlockDelta;
|
||||
}
|
||||
|
||||
export interface ContentBlockStopEvent {
|
||||
type: 'content_block_stop';
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface MessageStopStreamEvent {
|
||||
type: 'message_stop';
|
||||
}
|
||||
|
||||
export type StreamEvent =
|
||||
| MessageStartStreamEvent
|
||||
| ContentBlockStartEvent
|
||||
| ContentBlockDeltaEvent
|
||||
| ContentBlockStopEvent
|
||||
| MessageStopStreamEvent;
|
||||
|
||||
export interface SDKPartialAssistantMessage {
|
||||
type: 'stream_event';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
event: StreamEvent;
|
||||
parent_tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'yolo';
|
||||
|
||||
/**
|
||||
* TODO: Align with `ToolCallConfirmationDetails`
|
||||
*/
|
||||
export interface PermissionSuggestion {
|
||||
type: 'allow' | 'deny' | 'modify';
|
||||
label: string;
|
||||
description?: string;
|
||||
modifiedInput?: unknown;
|
||||
}
|
||||
|
||||
export interface HookRegistration {
|
||||
event: string;
|
||||
callback_id: string;
|
||||
}
|
||||
|
||||
export interface HookCallbackResult {
|
||||
shouldSkip?: boolean;
|
||||
shouldInterrupt?: boolean;
|
||||
suppressOutput?: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CLIControlInterruptRequest {
|
||||
subtype: 'interrupt';
|
||||
}
|
||||
|
||||
export interface CLIControlPermissionRequest {
|
||||
subtype: 'can_use_tool';
|
||||
tool_name: string;
|
||||
tool_use_id: string;
|
||||
input: unknown;
|
||||
permission_suggestions: PermissionSuggestion[] | null;
|
||||
blocked_path: string | null;
|
||||
}
|
||||
|
||||
export enum AuthProviderType {
|
||||
DYNAMIC_DISCOVERY = 'dynamic_discovery',
|
||||
GOOGLE_CREDENTIALS = 'google_credentials',
|
||||
SERVICE_ACCOUNT_IMPERSONATION = 'service_account_impersonation',
|
||||
}
|
||||
|
||||
export interface MCPServerConfig {
|
||||
command?: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
cwd?: string;
|
||||
url?: string;
|
||||
httpUrl?: string;
|
||||
headers?: Record<string, string>;
|
||||
tcp?: string;
|
||||
timeout?: number;
|
||||
trust?: boolean;
|
||||
description?: string;
|
||||
includeTools?: string[];
|
||||
excludeTools?: string[];
|
||||
extensionName?: string;
|
||||
oauth?: Record<string, unknown>;
|
||||
authProviderType?: AuthProviderType;
|
||||
targetAudience?: string;
|
||||
targetServiceAccount?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SDK MCP Server configuration
|
||||
*
|
||||
* SDK MCP servers run in the SDK process and are connected via in-memory transport.
|
||||
* Tool calls are routed through the control plane between SDK and CLI.
|
||||
*/
|
||||
export interface SDKMcpServerConfig {
|
||||
/**
|
||||
* Type identifier for SDK MCP servers
|
||||
*/
|
||||
type: 'sdk';
|
||||
/**
|
||||
* Server name for identification and routing
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The MCP Server instance created by createSdkMcpServer()
|
||||
*/
|
||||
instance: McpServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire format for SDK MCP servers sent to the CLI
|
||||
*/
|
||||
export type WireSDKMcpServerConfig = Omit<SDKMcpServerConfig, 'instance'>;
|
||||
|
||||
export interface CLIControlInitializeRequest {
|
||||
subtype: 'initialize';
|
||||
hooks?: HookRegistration[] | null;
|
||||
/**
|
||||
* SDK MCP servers config
|
||||
* These are MCP servers running in the SDK process, connected via control plane.
|
||||
* External MCP servers are configured separately in settings, not via initialization.
|
||||
*/
|
||||
sdkMcpServers?: Record<string, WireSDKMcpServerConfig>;
|
||||
/**
|
||||
* External MCP servers that should be managed by the CLI.
|
||||
*/
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
agents?: SubagentConfig[];
|
||||
}
|
||||
|
||||
export interface CLIControlSetPermissionModeRequest {
|
||||
subtype: 'set_permission_mode';
|
||||
mode: PermissionMode;
|
||||
}
|
||||
|
||||
export interface CLIHookCallbackRequest {
|
||||
subtype: 'hook_callback';
|
||||
callback_id: string;
|
||||
input: unknown;
|
||||
tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export interface CLIControlMcpMessageRequest {
|
||||
subtype: 'mcp_message';
|
||||
server_name: string;
|
||||
message: {
|
||||
jsonrpc?: string;
|
||||
method: string;
|
||||
params?: Record<string, unknown>;
|
||||
id?: string | number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CLIControlSetModelRequest {
|
||||
subtype: 'set_model';
|
||||
model: string;
|
||||
}
|
||||
|
||||
export interface CLIControlMcpStatusRequest {
|
||||
subtype: 'mcp_server_status';
|
||||
}
|
||||
|
||||
export interface CLIControlSupportedCommandsRequest {
|
||||
subtype: 'supported_commands';
|
||||
}
|
||||
|
||||
export type ControlRequestPayload =
|
||||
| CLIControlInterruptRequest
|
||||
| CLIControlPermissionRequest
|
||||
| CLIControlInitializeRequest
|
||||
| CLIControlSetPermissionModeRequest
|
||||
| CLIHookCallbackRequest
|
||||
| CLIControlMcpMessageRequest
|
||||
| CLIControlSetModelRequest
|
||||
| CLIControlMcpStatusRequest
|
||||
| CLIControlSupportedCommandsRequest;
|
||||
|
||||
export interface CLIControlRequest {
|
||||
type: 'control_request';
|
||||
request_id: string;
|
||||
request: ControlRequestPayload;
|
||||
}
|
||||
|
||||
export interface PermissionApproval {
|
||||
allowed: boolean;
|
||||
reason?: string;
|
||||
modifiedInput?: unknown;
|
||||
}
|
||||
|
||||
export interface ControlResponse {
|
||||
subtype: 'success';
|
||||
request_id: string;
|
||||
response: unknown;
|
||||
}
|
||||
|
||||
export interface ControlErrorResponse {
|
||||
subtype: 'error';
|
||||
request_id: string;
|
||||
error: string | { message: string; [key: string]: unknown };
|
||||
}
|
||||
|
||||
export interface CLIControlResponse {
|
||||
type: 'control_response';
|
||||
response: ControlResponse | ControlErrorResponse;
|
||||
}
|
||||
|
||||
export interface ControlCancelRequest {
|
||||
type: 'control_cancel_request';
|
||||
request_id?: string;
|
||||
}
|
||||
|
||||
export type ControlMessage =
|
||||
| CLIControlRequest
|
||||
| CLIControlResponse
|
||||
| ControlCancelRequest;
|
||||
|
||||
/**
|
||||
* Union of all SDK message types
|
||||
*/
|
||||
export type SDKMessage =
|
||||
| SDKUserMessage
|
||||
| SDKAssistantMessage
|
||||
| SDKSystemMessage
|
||||
| SDKResultMessage
|
||||
| SDKPartialAssistantMessage;
|
||||
|
||||
export function isSDKUserMessage(msg: any): msg is SDKUserMessage {
|
||||
return (
|
||||
msg && typeof msg === 'object' && msg.type === 'user' && 'message' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKAssistantMessage(msg: any): msg is SDKAssistantMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'assistant' &&
|
||||
'uuid' in msg &&
|
||||
'message' in msg &&
|
||||
'session_id' in msg &&
|
||||
'parent_tool_use_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKSystemMessage(msg: any): msg is SDKSystemMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'system' &&
|
||||
'subtype' in msg &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKResultMessage(msg: any): msg is SDKResultMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'result' &&
|
||||
'subtype' in msg &&
|
||||
'duration_ms' in msg &&
|
||||
'is_error' in msg &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKPartialAssistantMessage(
|
||||
msg: any,
|
||||
): msg is SDKPartialAssistantMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'stream_event' &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg &&
|
||||
'event' in msg &&
|
||||
'parent_tool_use_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlRequest(msg: any): msg is CLIControlRequest {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_request' &&
|
||||
'request_id' in msg &&
|
||||
'request' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlResponse(msg: any): msg is CLIControlResponse {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_response' &&
|
||||
'response' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlCancel(msg: any): msg is ControlCancelRequest {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_cancel_request' &&
|
||||
'request_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isTextBlock(block: any): block is TextBlock {
|
||||
return block && typeof block === 'object' && block.type === 'text';
|
||||
}
|
||||
|
||||
export function isThinkingBlock(block: any): block is ThinkingBlock {
|
||||
return block && typeof block === 'object' && block.type === 'thinking';
|
||||
}
|
||||
|
||||
export function isToolUseBlock(block: any): block is ToolUseBlock {
|
||||
return block && typeof block === 'object' && block.type === 'tool_use';
|
||||
}
|
||||
|
||||
export function isToolResultBlock(block: any): block is ToolResultBlock {
|
||||
return block && typeof block === 'object' && block.type === 'tool_result';
|
||||
}
|
||||
|
||||
export type SubagentLevel = 'session';
|
||||
|
||||
export interface ModelConfig {
|
||||
model?: string;
|
||||
temp?: number;
|
||||
top_p?: number;
|
||||
}
|
||||
|
||||
export interface RunConfig {
|
||||
max_time_minutes?: number;
|
||||
max_turns?: number;
|
||||
}
|
||||
|
||||
export interface SubagentConfig {
|
||||
name: string;
|
||||
description: string;
|
||||
tools?: string[];
|
||||
systemPrompt: string;
|
||||
level: SubagentLevel;
|
||||
filePath?: string;
|
||||
modelConfig?: Partial<ModelConfig>;
|
||||
runConfig?: Partial<RunConfig>;
|
||||
color?: string;
|
||||
readonly isBuiltin?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Control Request Types
|
||||
*
|
||||
* Centralized enum for all control request subtypes supported by the CLI.
|
||||
* This enum should be kept in sync with the controllers in:
|
||||
* - packages/cli/src/services/control/controllers/systemController.ts
|
||||
* - packages/cli/src/services/control/controllers/permissionController.ts
|
||||
* - packages/cli/src/services/control/controllers/mcpController.ts
|
||||
* - packages/cli/src/services/control/controllers/hookController.ts
|
||||
*/
|
||||
export enum ControlRequestType {
|
||||
// SystemController requests
|
||||
INITIALIZE = 'initialize',
|
||||
INTERRUPT = 'interrupt',
|
||||
SET_MODEL = 'set_model',
|
||||
SUPPORTED_COMMANDS = 'supported_commands',
|
||||
|
||||
// PermissionController requests
|
||||
CAN_USE_TOOL = 'can_use_tool',
|
||||
SET_PERMISSION_MODE = 'set_permission_mode',
|
||||
|
||||
// MCPController requests
|
||||
MCP_MESSAGE = 'mcp_message',
|
||||
MCP_SERVER_STATUS = 'mcp_server_status',
|
||||
|
||||
// HookController requests
|
||||
HOOK_CALLBACK = 'hook_callback',
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.session.event.SessionEventConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionCloseException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionStartException;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Session {
|
||||
private final Transport transport;
|
||||
private Capabilities capabilities;
|
||||
private static final Logger log = LoggerFactory.getLogger(Session.class);
|
||||
|
||||
public Session(Transport transport) throws SessionStartException {
|
||||
if (transport == null || !transport.isAvailable()) {
|
||||
throw new SessionStartException("Transport is not available");
|
||||
}
|
||||
this.transport = transport;
|
||||
start();
|
||||
}
|
||||
|
||||
private void start() throws SessionStartException {
|
||||
try {
|
||||
String response = transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
|
||||
CLIControlResponse<CLIControlInitializeResponse> cliControlResponse = JSON.parseObject(response, new TypeReference<CLIControlResponse<CLIControlInitializeResponse>>() {});
|
||||
this.capabilities = cliControlResponse.getResponse().getResponse().getCapabilities();
|
||||
} catch (Exception e) {
|
||||
throw new SessionStartException("Failed to initialize the session", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws SessionCloseException {
|
||||
try {
|
||||
transport.close();
|
||||
} catch (Exception e) {
|
||||
throw new SessionCloseException("Failed to close the session", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Capabilities getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public void sendPrompt(String prompt, SessionEventConsumers sessionEventConsumers) throws SessionSendPromptException {
|
||||
if (!transport.isAvailable()) {
|
||||
throw new SessionSendPromptException("Session is not available");
|
||||
}
|
||||
|
||||
try {
|
||||
transport.inputWaitForMultiLine(new SDKUserMessage().setContent(prompt).toString(), (line) -> {
|
||||
log.debug("read a message from agent {}", line);
|
||||
JSONObject jsonObject = JSON.parseObject(line);
|
||||
|
||||
String messageType = jsonObject.getString("type");
|
||||
if ("system".equals(messageType)) {
|
||||
sessionEventConsumers.onSystemMessage(JSON.parseObject(line, SDKSystemMessage.class));
|
||||
return false;
|
||||
} else if ("assistant".equals(messageType)) {
|
||||
sessionEventConsumers.onAssistantMessage(JSON.parseObject(line, SDKAssistantMessage.class));
|
||||
return false;
|
||||
} else if ("result".equals(messageType)) {
|
||||
sessionEventConsumers.onResultMessage(JSON.parseObject(line, SDKResultMessage.class));
|
||||
return true;
|
||||
} else {
|
||||
log.warn("unknown message type: {}", messageType);
|
||||
sessionEventConsumers.onOtherMessage(line);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new SessionSendPromptException("Failed to send prompt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session.event;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
|
||||
public interface SessionEventConsumers {
|
||||
void onSystemMessage(SDKSystemMessage systemMessage);
|
||||
|
||||
void onResultMessage(SDKResultMessage resultMessage);
|
||||
|
||||
void onAssistantMessage(SDKAssistantMessage assistantMessage);
|
||||
|
||||
void onOtherMessage(String message);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session.event;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
|
||||
public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
||||
@Override
|
||||
public void onSystemMessage(SDKSystemMessage systemMessage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResultMessage(SDKResultMessage resultMessage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherMessage(String message) {
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session.exception;
|
||||
|
||||
public class SessionCloseException extends Exception {
|
||||
public SessionCloseException() {
|
||||
}
|
||||
|
||||
public SessionCloseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SessionCloseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public SessionCloseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SessionCloseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session.exception;
|
||||
|
||||
public class SessionSendPromptException extends Exception {
|
||||
public SessionSendPromptException() {
|
||||
}
|
||||
|
||||
public SessionSendPromptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SessionSendPromptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public SessionSendPromptException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SessionSendPromptException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session.exception;
|
||||
|
||||
public class SessionStartException extends Exception {
|
||||
public SessionStartException() {
|
||||
}
|
||||
|
||||
public SessionStartException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SessionStartException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public SessionStartException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SessionStartException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
public enum PermissionMode {
|
||||
DEFAULT("default"),
|
||||
PLAN("plan"),
|
||||
AUTO_EDIT("auto-edit"),
|
||||
YOLO("yolo");
|
||||
|
||||
private final String value;
|
||||
|
||||
PermissionMode(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static PermissionMode fromValue(String value) {
|
||||
for (PermissionMode mode : PermissionMode.values()) {
|
||||
if (mode.value.equals(value)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown permission mode: " + value);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface Transport {
|
||||
void close() throws IOException;
|
||||
|
||||
boolean isAvailable();
|
||||
|
||||
String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException;
|
||||
|
||||
void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException;
|
||||
|
||||
void inputNoWaitResponse(String message) throws IOException;
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransportOptions implements Cloneable {
|
||||
private String pathToQwenExecutable;
|
||||
private String cwd;
|
||||
private String model;
|
||||
private PermissionMode permissionMode;
|
||||
private Map<String, String> env;
|
||||
private Integer maxSessionTurns;
|
||||
private List<String> coreTools;
|
||||
private List<String> excludeTools;
|
||||
private List<String> allowedTools;
|
||||
private String authType;
|
||||
private Boolean includePartialMessages;
|
||||
private Long turnTimeoutMs;
|
||||
private Long messageTimeoutMs;
|
||||
|
||||
public String getPathToQwenExecutable() {
|
||||
return pathToQwenExecutable;
|
||||
}
|
||||
|
||||
public void setPathToQwenExecutable(String pathToQwenExecutable) {
|
||||
this.pathToQwenExecutable = pathToQwenExecutable;
|
||||
}
|
||||
|
||||
public String getCwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
public void setCwd(String cwd) {
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public PermissionMode getPermissionMode() {
|
||||
return permissionMode;
|
||||
}
|
||||
|
||||
public void setPermissionMode(PermissionMode permissionMode) {
|
||||
this.permissionMode = permissionMode;
|
||||
}
|
||||
|
||||
public Map<String, String> getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
public void setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public Integer getMaxSessionTurns() {
|
||||
return maxSessionTurns;
|
||||
}
|
||||
|
||||
public void setMaxSessionTurns(Integer maxSessionTurns) {
|
||||
this.maxSessionTurns = maxSessionTurns;
|
||||
}
|
||||
|
||||
public List<String> getCoreTools() {
|
||||
return coreTools;
|
||||
}
|
||||
|
||||
public void setCoreTools(List<String> coreTools) {
|
||||
this.coreTools = coreTools;
|
||||
}
|
||||
|
||||
public List<String> getExcludeTools() {
|
||||
return excludeTools;
|
||||
}
|
||||
|
||||
public void setExcludeTools(List<String> excludeTools) {
|
||||
this.excludeTools = excludeTools;
|
||||
}
|
||||
|
||||
public List<String> getAllowedTools() {
|
||||
return allowedTools;
|
||||
}
|
||||
|
||||
public void setAllowedTools(List<String> allowedTools) {
|
||||
this.allowedTools = allowedTools;
|
||||
}
|
||||
|
||||
public String getAuthType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
public void setAuthType(String authType) {
|
||||
this.authType = authType;
|
||||
}
|
||||
|
||||
public Boolean getIncludePartialMessages() {
|
||||
return includePartialMessages;
|
||||
}
|
||||
|
||||
public void setIncludePartialMessages(Boolean includePartialMessages) {
|
||||
this.includePartialMessages = includePartialMessages;
|
||||
}
|
||||
|
||||
public Long getTurnTimeoutMs() {
|
||||
return turnTimeoutMs;
|
||||
}
|
||||
|
||||
public void setTurnTimeoutMs(Long turnTimeoutMs) {
|
||||
this.turnTimeoutMs = turnTimeoutMs;
|
||||
}
|
||||
|
||||
public Long getMessageTimeoutMs() {
|
||||
return messageTimeoutMs;
|
||||
}
|
||||
|
||||
public void setMessageTimeoutMs(Long messageTimeoutMs) {
|
||||
this.messageTimeoutMs = messageTimeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportOptions clone() {
|
||||
try {
|
||||
return (TransportOptions) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
import org.apache.commons.lang3.exception.ContextedRuntimeException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ProcessTransport implements Transport {
|
||||
private static final Logger log = LoggerFactory.getLogger(ProcessTransport.class);
|
||||
TransportOptionsAdapter transportOptionsAdapter;
|
||||
|
||||
protected final Long turnTimeoutMs;
|
||||
protected final Long messageTimeoutMs;
|
||||
|
||||
protected Process process;
|
||||
protected BufferedWriter processInput;
|
||||
protected BufferedReader processOutput;
|
||||
protected BufferedReader processError;
|
||||
|
||||
public ProcessTransport() throws IOException {
|
||||
this(new TransportOptions());
|
||||
}
|
||||
|
||||
public ProcessTransport(TransportOptions transportOptions) throws IOException {
|
||||
this.transportOptionsAdapter = new TransportOptionsAdapter(transportOptions);
|
||||
turnTimeoutMs = transportOptionsAdapter.getHandledTransportOptions().getTurnTimeoutMs();
|
||||
messageTimeoutMs = transportOptionsAdapter.getHandledTransportOptions().getMessageTimeoutMs();
|
||||
start();
|
||||
}
|
||||
|
||||
protected void start() throws IOException {
|
||||
String[] commandArgs = transportOptionsAdapter.buildCommandArgs();
|
||||
log.debug("trans to command args: {}", transportOptionsAdapter);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(commandArgs)
|
||||
.redirectOutput(Redirect.PIPE)
|
||||
.redirectInput(Redirect.PIPE)
|
||||
.redirectError(Redirect.PIPE)
|
||||
.redirectErrorStream(false)
|
||||
.directory(new File(transportOptionsAdapter.getCwd()));
|
||||
|
||||
process = processBuilder.start();
|
||||
processInput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
|
||||
processOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
processError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
startErrorReading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (processInput != null) {
|
||||
processInput.close();
|
||||
}
|
||||
if (processOutput != null) {
|
||||
processOutput.close();
|
||||
}
|
||||
if (processError != null) {
|
||||
processError.close();
|
||||
}
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return process != null && process.isAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
return inputWaitForOneLine(message, turnTimeoutMs);
|
||||
}
|
||||
|
||||
private String inputWaitForOneLine(String message, long timeOutInMs)
|
||||
throws IOException, TimeoutException, InterruptedException, ExecutionException {
|
||||
inputNoWaitResponse(message);
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return processOutput.readLine();
|
||||
} catch (IOException e) {
|
||||
throw new ContextedRuntimeException("read line error", e)
|
||||
.addContextValue("message", message);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
String line = future.get(timeOutInMs, TimeUnit.MILLISECONDS);
|
||||
log.info("inputWaitForOneLine result: {}", line);
|
||||
return line;
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
log.warn("read message timeout {}, canceled readOneLine task", timeOutInMs, e);
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
future.cancel(true);
|
||||
log.warn("interrupted task, canceled task", e);
|
||||
throw e;
|
||||
} catch (ExecutionException e) {
|
||||
future.cancel(true);
|
||||
log.warn("the readOneLine task execute error", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException {
|
||||
inputWaitForMultiLine(message, callBackFunction, turnTimeoutMs);
|
||||
}
|
||||
|
||||
private void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction, long timeOutInMs) throws IOException {
|
||||
log.debug("input message for multiLine: {}", message);
|
||||
inputNoWaitResponse(message);
|
||||
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> iterateOutput(callBackFunction));
|
||||
try {
|
||||
future.get(timeOutInMs, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
log.warn("read message timeout {}, canceled readMultiMessages task", timeOutInMs, e);
|
||||
} catch (InterruptedException e) {
|
||||
future.cancel(true);
|
||||
log.warn("interrupted task, canceled task", e);
|
||||
} catch (ExecutionException e) {
|
||||
future.cancel(true);
|
||||
log.warn("the readMultiMessages task execute error", e);
|
||||
} catch (Exception e) {
|
||||
future.cancel(true);
|
||||
log.warn("other error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputNoWaitResponse(String message) throws IOException {
|
||||
log.debug("input message to agent: {}", message);
|
||||
processInput.write(message);
|
||||
processInput.newLine();
|
||||
processInput.flush();
|
||||
}
|
||||
|
||||
private void startErrorReading() {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
String line;
|
||||
while ((line = processError.readLine()) != null) {
|
||||
System.err.println("错误: " + line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("错误: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void iterateOutput(Function<String, Boolean> callBackFunction) {
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
for (String line = processOutput.readLine(); line != null; line = processOutput.readLine()) {
|
||||
log.debug("read a message from agent {}", line);
|
||||
if (callBackFunction.apply(line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("read process output error", e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
future.get(messageTimeoutMs, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("read message task interrupted", e);
|
||||
future.cancel(true);
|
||||
} catch (TimeoutException e) {
|
||||
log.warn("Operation timed out", e);
|
||||
future.cancel(true);
|
||||
} catch (Exception e) {
|
||||
future.cancel(true);
|
||||
log.warn("Operation error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
class TransportOptionsAdapter {
|
||||
TransportOptions transportOptions;
|
||||
private static final Long DEFAULT_TURN_TIMEOUT_MS = 1000 * 60 * 30L;
|
||||
private static final Long DEFAULT_MESSAGE_TIMEOUT_MS = 1000 * 60 * 3L;
|
||||
|
||||
TransportOptionsAdapter(TransportOptions userTransportOptions) {
|
||||
transportOptions = addDefaultTransportOptions(userTransportOptions);
|
||||
}
|
||||
|
||||
TransportOptions getHandledTransportOptions() {
|
||||
return transportOptions;
|
||||
}
|
||||
|
||||
String getCwd() {
|
||||
return transportOptions.getCwd();
|
||||
}
|
||||
|
||||
String[] buildCommandArgs() {
|
||||
List<String> args = new ArrayList<>(
|
||||
Arrays.asList(transportOptions.getPathToQwenExecutable(), "--input-format", "stream-json", "--output-format",
|
||||
"stream-json", "--channel=SDK"));
|
||||
|
||||
if (StringUtils.isNotBlank(transportOptions.getModel())) {
|
||||
args.add("--model");
|
||||
args.add(transportOptions.getModel());
|
||||
}
|
||||
|
||||
if (transportOptions.getPermissionMode() != null) {
|
||||
args.add("--permission-mode");
|
||||
args.add(transportOptions.getPermissionMode().getValue());
|
||||
}
|
||||
|
||||
if (transportOptions.getMaxSessionTurns() != null) {
|
||||
args.add("--max-session-turns");
|
||||
args.add(transportOptions.getMaxSessionTurns().toString());
|
||||
}
|
||||
|
||||
if (transportOptions.getCoreTools() != null && !transportOptions.getCoreTools().isEmpty()) {
|
||||
args.add("--core-tools");
|
||||
args.add(String.join(",", transportOptions.getCoreTools()));
|
||||
}
|
||||
|
||||
if (transportOptions.getExcludeTools() != null && !transportOptions.getExcludeTools().isEmpty()) {
|
||||
args.add("--exclude-tools");
|
||||
args.add(String.join(",", transportOptions.getExcludeTools()));
|
||||
}
|
||||
|
||||
if (transportOptions.getAllowedTools() != null && !transportOptions.getAllowedTools().isEmpty()) {
|
||||
args.add("--allowed-tools");
|
||||
args.add(String.join(",", transportOptions.getAllowedTools()));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(transportOptions.getAuthType())) {
|
||||
args.add("--auth-type");
|
||||
args.add(transportOptions.getAuthType());
|
||||
}
|
||||
|
||||
if (transportOptions.getIncludePartialMessages() != null && transportOptions.getIncludePartialMessages()) {
|
||||
args.add("--include-partial-messages");
|
||||
}
|
||||
return args.toArray(new String[] {});
|
||||
}
|
||||
|
||||
private TransportOptions addDefaultTransportOptions(TransportOptions userTransportOptions) {
|
||||
TransportOptions transportOptions = Optional.ofNullable(userTransportOptions)
|
||||
.map(TransportOptions::clone)
|
||||
.orElse(new TransportOptions());
|
||||
|
||||
if (StringUtils.isBlank(transportOptions.getPathToQwenExecutable())) {
|
||||
transportOptions.setPathToQwenExecutable("qwen");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(transportOptions.getCwd())) {
|
||||
transportOptions.setCwd(new File("").getAbsolutePath());
|
||||
}
|
||||
|
||||
Map<String, String> env = new HashMap<>(System.getenv());
|
||||
Optional.ofNullable(transportOptions.getEnv()).ifPresent(env::putAll);
|
||||
transportOptions.setEnv(env);
|
||||
|
||||
if (transportOptions.getTurnTimeoutMs() == null) {
|
||||
transportOptions.setTurnTimeoutMs(DEFAULT_TURN_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
if (transportOptions.getMessageTimeoutMs() == null) {
|
||||
transportOptions.setMessageTimeoutMs(DEFAULT_MESSAGE_TIMEOUT_MS);
|
||||
}
|
||||
return transportOptions;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.message.Message;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class QwenCliTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QwenCliTest.class);
|
||||
@Test
|
||||
void query() {
|
||||
List<Message> result = QwenCli.query("hello world");
|
||||
log.info("result: {}", result);
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.session.event.SessionEventConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionCloseException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionStartException;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class SessionTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionTest.class);
|
||||
@Test
|
||||
void sendPrompt() throws IOException, SessionStartException, SessionSendPromptException, SessionCloseException {
|
||||
Transport transport = new ProcessTransport();
|
||||
Session session = new Session(transport);
|
||||
session.sendPrompt("hello world", new SessionEventSimpleConsumers() {
|
||||
@Override
|
||||
public void onSystemMessage(SDKSystemMessage systemMessage) {
|
||||
log.info("systemMessage: {}", systemMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResultMessage(SDKResultMessage resultMessage) {
|
||||
log.info("resultMessage: {}", resultMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantMessage(SDKAssistantMessage assistantMessage) {
|
||||
log.info("assistantMessage: {}", assistantMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherMessage(String message) {
|
||||
log.info("otherMessage: {}", message);
|
||||
}
|
||||
});
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJSON() {
|
||||
String json = "{\"type\":\"assistant\",\"uuid\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\",\"session_id\":\"166badc0-e6d3-4978-ae47-4ccd51c468ef\",\"message\":{\"content\":[{\"text\":\"Hello! How can I help you with the Qwen Code SDK for Java today?\",\"type\":\"text\"}],\"id\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\",\"model\":\"qwen3-coder-plus\",\"role\":\"assistant\",\"type\":\"message\",\"usage\":{\"cache_read_input_tokens\":12766,\"input_tokens\":12770,\"output_tokens\":17,\"total_tokens\":12787}}}";
|
||||
SDKAssistantMessage assistantMessage = JSON.parseObject(json, SDKAssistantMessage.class);
|
||||
log.info("the assistantMessage: {}", assistantMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PermissionModeTest {
|
||||
|
||||
@Test
|
||||
public void shouldBeReturnQwenPermissionModeValue() {
|
||||
assertEquals("default", PermissionMode.DEFAULT.getValue());
|
||||
assertEquals("plan", PermissionMode.PLAN.getValue());
|
||||
assertEquals("auto-edit", PermissionMode.AUTO_EDIT.getValue());
|
||||
assertEquals("yolo", PermissionMode.YOLO.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class ProcessTransportTest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProcessTransportTest.class);
|
||||
|
||||
@Test
|
||||
void shouldStartAndCloseSuccessfully() throws IOException {
|
||||
TransportOptions transportOptions = new TransportOptions();
|
||||
Transport transport = new ProcessTransport(transportOptions);
|
||||
transport.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInputWaitForOneLineSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
TransportOptions transportOptions = new TransportOptions();
|
||||
Transport transport = new ProcessTransport(transportOptions);
|
||||
|
||||
String message = "{\"type\": \"control_request\", \"request_id\": \"1\", \"request\": {\"subtype\": \"initialize\"} }";
|
||||
System.out.println(transport.inputWaitForOneLine(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInitializeSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
Transport transport = new ProcessTransport();
|
||||
|
||||
String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
|
||||
String responseMsg = transport.inputWaitForOneLine(message);
|
||||
logger.info("responseMsg: {}", responseMsg);
|
||||
CLIControlResponse<CLIControlInitializeResponse> response = JSON.parseObject(responseMsg,
|
||||
new TypeReference<CLIControlResponse<CLIControlInitializeResponse>>() {});
|
||||
logger.info("response: {}", response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSdkMessageSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
Transport transport = new ProcessTransport();
|
||||
String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
|
||||
transport.inputWaitForOneLine(message);
|
||||
|
||||
String sessionId = "session-" + UUID.randomUUID().toString();
|
||||
String userMessage = new SDKUserMessage().setSessionId(sessionId).setContent("hello world").toString();
|
||||
transport.inputWaitForMultiLine(userMessage, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage2 = new SDKUserMessage().setSessionId(sessionId).setContent("请使用中文").toString();
|
||||
transport.inputWaitForMultiLine(userMessage2, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage3 = new SDKUserMessage().setSessionId(sessionId).setContent("当前工作区有多少个文件").toString();
|
||||
transport.inputWaitForMultiLine(userMessage3, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage4 = new SDKUserMessage().setSessionId("session-sec" + UUID.randomUUID()).setContent("有多少个xml文件").toString();
|
||||
transport.inputWaitForMultiLine(userMessage4, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
|
||||
transport.inputWaitForMultiLine(new SDKUserMessage().setContent("您好").toString(),
|
||||
line -> "result".equals(JSON.parseObject(line).getString("type")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -150,48 +150,49 @@ export function parseExecutableSpec(executableSpec?: string): {
|
||||
}
|
||||
|
||||
// Check for runtime prefix (e.g., 'bun:/path/to/cli.js')
|
||||
// Use whitelist mechanism: only treat as runtime spec if prefix matches supported runtimes
|
||||
const supportedRuntimes = ['node', 'bun', 'tsx', 'deno'];
|
||||
const runtimeMatch = executableSpec.match(/^([^:]+):(.+)$/);
|
||||
|
||||
if (runtimeMatch) {
|
||||
const [, runtime, filePath] = runtimeMatch;
|
||||
if (!runtime || !filePath) {
|
||||
throw new Error(`Invalid runtime specification: '${executableSpec}'`);
|
||||
|
||||
// Only process as runtime specification if it matches a supported runtime
|
||||
if (runtime && supportedRuntimes.includes(runtime)) {
|
||||
if (!filePath) {
|
||||
throw new Error(`Invalid runtime specification: '${executableSpec}'`);
|
||||
}
|
||||
|
||||
if (!validateRuntimeAvailability(runtime)) {
|
||||
throw new Error(
|
||||
`Runtime '${runtime}' is not available on this system. Please install it first.`,
|
||||
);
|
||||
}
|
||||
|
||||
const resolvedPath = path.resolve(filePath);
|
||||
|
||||
if (!fs.existsSync(resolvedPath)) {
|
||||
throw new Error(
|
||||
`Executable file not found at '${resolvedPath}' for runtime '${runtime}'. ` +
|
||||
'Please check the file path and ensure the file exists.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!validateFileExtensionForRuntime(resolvedPath, runtime)) {
|
||||
const ext = path.extname(resolvedPath);
|
||||
throw new Error(
|
||||
`File extension '${ext}' is not compatible with runtime '${runtime}'. ` +
|
||||
`Expected extensions for ${runtime}: ${getExpectedExtensions(runtime).join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
runtime,
|
||||
executablePath: resolvedPath,
|
||||
isExplicitRuntime: true,
|
||||
};
|
||||
}
|
||||
|
||||
const supportedRuntimes = ['node', 'bun', 'tsx', 'deno'];
|
||||
if (!supportedRuntimes.includes(runtime)) {
|
||||
throw new Error(
|
||||
`Unsupported runtime '${runtime}'. Supported runtimes: ${supportedRuntimes.join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!validateRuntimeAvailability(runtime)) {
|
||||
throw new Error(
|
||||
`Runtime '${runtime}' is not available on this system. Please install it first.`,
|
||||
);
|
||||
}
|
||||
|
||||
const resolvedPath = path.resolve(filePath);
|
||||
|
||||
if (!fs.existsSync(resolvedPath)) {
|
||||
throw new Error(
|
||||
`Executable file not found at '${resolvedPath}' for runtime '${runtime}'. ` +
|
||||
'Please check the file path and ensure the file exists.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!validateFileExtensionForRuntime(resolvedPath, runtime)) {
|
||||
const ext = path.extname(resolvedPath);
|
||||
throw new Error(
|
||||
`File extension '${ext}' is not compatible with runtime '${runtime}'. ` +
|
||||
`Expected extensions for ${runtime}: ${getExpectedExtensions(runtime).join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
runtime,
|
||||
executablePath: resolvedPath,
|
||||
isExplicitRuntime: true,
|
||||
};
|
||||
// If not a supported runtime, fall through to treat as file path (e.g., Windows paths like 'D:\path\to\cli.js')
|
||||
}
|
||||
|
||||
// Check if it's a command name (no path separators) or a file path
|
||||
|
||||
@@ -125,12 +125,43 @@ describe('CLI Path Utilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw for invalid runtime prefix format', () => {
|
||||
it('should treat non-whitelisted runtime prefixes as command names', () => {
|
||||
// With whitelist approach, 'invalid:format' is not recognized as a runtime spec
|
||||
// so it's treated as a command name, which fails validation due to the colon
|
||||
expect(() => parseExecutableSpec('invalid:format')).toThrow(
|
||||
'Unsupported runtime',
|
||||
'Invalid command name',
|
||||
);
|
||||
});
|
||||
|
||||
it('should treat Windows drive letters as file paths, not runtime specs', () => {
|
||||
mockFs.existsSync.mockReturnValue(true);
|
||||
|
||||
// Test various Windows drive letters
|
||||
const windowsPaths = [
|
||||
'C:\\path\\to\\cli.js',
|
||||
'D:\\path\\to\\cli.js',
|
||||
'E:\\Users\\dev\\qwen\\cli.js',
|
||||
];
|
||||
|
||||
for (const winPath of windowsPaths) {
|
||||
const result = parseExecutableSpec(winPath);
|
||||
|
||||
expect(result.isExplicitRuntime).toBe(false);
|
||||
expect(result.runtime).toBeUndefined();
|
||||
expect(result.executablePath).toBe(path.resolve(winPath));
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle Windows paths with forward slashes', () => {
|
||||
mockFs.existsSync.mockReturnValue(true);
|
||||
|
||||
const result = parseExecutableSpec('C:/path/to/cli.js');
|
||||
|
||||
expect(result.isExplicitRuntime).toBe(false);
|
||||
expect(result.runtime).toBeUndefined();
|
||||
expect(result.executablePath).toBe(path.resolve('C:/path/to/cli.js'));
|
||||
});
|
||||
|
||||
it('should throw when runtime-prefixed file does not exist', () => {
|
||||
mockFs.existsSync.mockReturnValue(false);
|
||||
|
||||
@@ -453,6 +484,41 @@ describe('CLI Path Utilities', () => {
|
||||
originalInput: `bun:${bundlePath}`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle Windows paths with drive letters', () => {
|
||||
const windowsPath = 'D:\\path\\to\\cli.js';
|
||||
const result = prepareSpawnInfo(windowsPath);
|
||||
|
||||
expect(result).toEqual({
|
||||
command: process.execPath,
|
||||
args: [path.resolve(windowsPath)],
|
||||
type: 'node',
|
||||
originalInput: windowsPath,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle Windows paths with TypeScript files', () => {
|
||||
const windowsPath = 'C:\\Users\\dev\\qwen\\index.ts';
|
||||
const result = prepareSpawnInfo(windowsPath);
|
||||
|
||||
expect(result).toEqual({
|
||||
command: 'tsx',
|
||||
args: [path.resolve(windowsPath)],
|
||||
type: 'tsx',
|
||||
originalInput: windowsPath,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not confuse Windows drive letters with runtime prefixes', () => {
|
||||
// Ensure 'D:' is not treated as a runtime specification
|
||||
const windowsPath = 'D:\\workspace\\project\\cli.js';
|
||||
const result = prepareSpawnInfo(windowsPath);
|
||||
|
||||
// Should use node runtime based on .js extension, not treat 'D' as runtime
|
||||
expect(result.type).toBe('node');
|
||||
expect(result.command).toBe(process.execPath);
|
||||
expect(result.args).toEqual([path.resolve(windowsPath)]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error cases', () => {
|
||||
@@ -472,21 +538,39 @@ describe('CLI Path Utilities', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should provide helpful error for invalid runtime specification', () => {
|
||||
it('should treat non-whitelisted runtime prefixes as command names', () => {
|
||||
// With whitelist approach, 'invalid:spec' is not recognized as a runtime spec
|
||||
// so it's treated as a command name, which fails validation due to the colon
|
||||
expect(() => prepareSpawnInfo('invalid:spec')).toThrow(
|
||||
'Unsupported runtime',
|
||||
'Invalid command name',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle Windows paths correctly even when file is missing', () => {
|
||||
mockFs.existsSync.mockReturnValue(false);
|
||||
|
||||
expect(() => prepareSpawnInfo('D:\\missing\\cli.js')).toThrow(
|
||||
'Executable file not found at',
|
||||
);
|
||||
// Should not throw 'Invalid command name' error (which would happen if 'D:' was treated as invalid command)
|
||||
expect(() => prepareSpawnInfo('D:\\missing\\cli.js')).not.toThrow(
|
||||
'Invalid command name',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('comprehensive validation', () => {
|
||||
describe('runtime validation', () => {
|
||||
it('should reject unsupported runtimes', () => {
|
||||
expect(() =>
|
||||
parseExecutableSpec('unsupported:/path/to/file.js'),
|
||||
).toThrow(
|
||||
"Unsupported runtime 'unsupported'. Supported runtimes: node, bun, tsx, deno",
|
||||
);
|
||||
it('should treat unsupported runtime prefixes as file paths', () => {
|
||||
mockFs.existsSync.mockReturnValue(true);
|
||||
|
||||
// With whitelist approach, 'unsupported:' is not recognized as a runtime spec
|
||||
// so 'unsupported:/path/to/file.js' is treated as a file path
|
||||
const result = parseExecutableSpec('unsupported:/path/to/file.js');
|
||||
|
||||
// Should be treated as a file path, not a runtime specification
|
||||
expect(result.isExplicitRuntime).toBe(false);
|
||||
expect(result.runtime).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should validate runtime availability for explicit runtime specs', () => {
|
||||
|
||||
Reference in New Issue
Block a user