noema-cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp/handler.js +164 -0
- package/dist/acp/index.js +5 -0
- package/dist/acp/io.js +71 -0
- package/dist/acp/state.js +14 -0
- package/dist/acp/types.js +1 -0
- package/dist/core/index.js +1 -0
- package/dist/index.js +7 -0
- package/dist/main.js +3 -0
- package/package.json +19 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { ensureSession, generateSessionId } from "./state.js";
|
|
2
|
+
export const handleRequest = (request, state) => {
|
|
3
|
+
switch (request.method) {
|
|
4
|
+
case "initialize": {
|
|
5
|
+
const params = asObject(request.params);
|
|
6
|
+
const protocolVersion = extractProtocolVersion(params);
|
|
7
|
+
return {
|
|
8
|
+
responses: [
|
|
9
|
+
{
|
|
10
|
+
jsonrpc: "2.0",
|
|
11
|
+
id: request.id,
|
|
12
|
+
result: {
|
|
13
|
+
protocolVersion,
|
|
14
|
+
agentCapabilities: {
|
|
15
|
+
loadSession: false,
|
|
16
|
+
mcpCapabilities: {
|
|
17
|
+
http: false,
|
|
18
|
+
sse: false,
|
|
19
|
+
},
|
|
20
|
+
promptCapabilities: {
|
|
21
|
+
audio: false,
|
|
22
|
+
embeddedContext: false,
|
|
23
|
+
image: false,
|
|
24
|
+
},
|
|
25
|
+
sessionCapabilities: {},
|
|
26
|
+
},
|
|
27
|
+
agentInfo: {
|
|
28
|
+
name: "noema-cli",
|
|
29
|
+
title: "Noema CLI",
|
|
30
|
+
version: "0.0.1",
|
|
31
|
+
},
|
|
32
|
+
authMethods: [],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
notifications: [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
case "session/new": {
|
|
40
|
+
const sessionId = generateSessionId(state);
|
|
41
|
+
ensureSession(state, sessionId);
|
|
42
|
+
return {
|
|
43
|
+
responses: [
|
|
44
|
+
{
|
|
45
|
+
jsonrpc: "2.0",
|
|
46
|
+
id: request.id,
|
|
47
|
+
result: {
|
|
48
|
+
sessionId,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
notifications: [],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
case "session/prompt": {
|
|
56
|
+
const params = asObject(request.params);
|
|
57
|
+
const sessionId = typeof params?.sessionId === "string" ? params.sessionId : null;
|
|
58
|
+
if (sessionId === null) {
|
|
59
|
+
return invalidParamsResponse(request.id);
|
|
60
|
+
}
|
|
61
|
+
ensureSession(state, sessionId);
|
|
62
|
+
const promptText = extractPromptText(params);
|
|
63
|
+
const responseText = buildEchoResponse(promptText);
|
|
64
|
+
const updates = buildStreamingUpdates(sessionId, responseText);
|
|
65
|
+
return {
|
|
66
|
+
responses: [
|
|
67
|
+
{
|
|
68
|
+
jsonrpc: "2.0",
|
|
69
|
+
id: request.id,
|
|
70
|
+
result: {
|
|
71
|
+
stopReason: "end_turn",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
notifications: updates,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
default:
|
|
79
|
+
return {
|
|
80
|
+
responses: [
|
|
81
|
+
{
|
|
82
|
+
jsonrpc: "2.0",
|
|
83
|
+
id: request.id,
|
|
84
|
+
error: {
|
|
85
|
+
code: -32601,
|
|
86
|
+
message: "Method not found",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
notifications: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const asObject = (value) => isRecord(value) ? value : null;
|
|
95
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
96
|
+
const extractPromptText = (params) => {
|
|
97
|
+
if (params === null) {
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
const promptBlocks = Array.isArray(params.prompt) ? params.prompt : [];
|
|
101
|
+
return promptBlocks.map(extractTextFromBlock).join("");
|
|
102
|
+
};
|
|
103
|
+
const buildEchoResponse = (promptText) => promptText.length === 0 ? "" : `echo: ${promptText}`;
|
|
104
|
+
const buildStreamingUpdates = (sessionId, responseText) => {
|
|
105
|
+
const chunks = splitResponse(responseText);
|
|
106
|
+
return chunks.map((chunk) => ({
|
|
107
|
+
jsonrpc: "2.0",
|
|
108
|
+
method: "session/update",
|
|
109
|
+
params: {
|
|
110
|
+
sessionId,
|
|
111
|
+
update: {
|
|
112
|
+
sessionUpdate: "agent_message_chunk",
|
|
113
|
+
content: {
|
|
114
|
+
type: "text",
|
|
115
|
+
text: chunk,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
}));
|
|
120
|
+
};
|
|
121
|
+
const splitResponse = (responseText) => {
|
|
122
|
+
if (responseText.length === 0) {
|
|
123
|
+
return [""];
|
|
124
|
+
}
|
|
125
|
+
if (responseText.length === 1) {
|
|
126
|
+
return [responseText];
|
|
127
|
+
}
|
|
128
|
+
const midpoint = Math.floor(responseText.length / 2);
|
|
129
|
+
return [responseText.slice(0, midpoint), responseText.slice(midpoint)];
|
|
130
|
+
};
|
|
131
|
+
const extractTextFromBlock = (block) => {
|
|
132
|
+
if (!isRecord(block)) {
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
136
|
+
return block.text;
|
|
137
|
+
}
|
|
138
|
+
if (isRecord(block.content)) {
|
|
139
|
+
const content = block.content;
|
|
140
|
+
if (content.type === "text" && typeof content.text === "string") {
|
|
141
|
+
return content.text;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return "";
|
|
145
|
+
};
|
|
146
|
+
const extractProtocolVersion = (params) => {
|
|
147
|
+
if (params === null) {
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
return typeof params.protocolVersion === "number" ? params.protocolVersion : 1;
|
|
151
|
+
};
|
|
152
|
+
const invalidParamsResponse = (id) => ({
|
|
153
|
+
responses: [
|
|
154
|
+
{
|
|
155
|
+
jsonrpc: "2.0",
|
|
156
|
+
id,
|
|
157
|
+
error: {
|
|
158
|
+
code: -32602,
|
|
159
|
+
message: "Invalid params",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
notifications: [],
|
|
164
|
+
});
|
package/dist/acp/io.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { handleRequest } from "./handler.js";
|
|
3
|
+
import { createAdapterState } from "./state.js";
|
|
4
|
+
export const runAcpAdapter = (input = process.stdin, output = process.stdout, state = createAdapterState()) => {
|
|
5
|
+
const rl = createInterface({ input });
|
|
6
|
+
rl.on("line", (line) => {
|
|
7
|
+
const trimmed = line.trim();
|
|
8
|
+
if (trimmed.length === 0) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
let message;
|
|
12
|
+
try {
|
|
13
|
+
message = JSON.parse(trimmed);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
writeMessage(output, {
|
|
17
|
+
jsonrpc: "2.0",
|
|
18
|
+
id: null,
|
|
19
|
+
error: {
|
|
20
|
+
code: -32700,
|
|
21
|
+
message: "Parse error",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!isJsonRpcRequest(message)) {
|
|
27
|
+
if (hasId(message)) {
|
|
28
|
+
writeMessage(output, {
|
|
29
|
+
jsonrpc: "2.0",
|
|
30
|
+
id: message.id ?? null,
|
|
31
|
+
error: {
|
|
32
|
+
code: -32600,
|
|
33
|
+
message: "Invalid Request",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const { responses, notifications } = handleRequest(message, state);
|
|
40
|
+
for (const notification of notifications) {
|
|
41
|
+
writeMessage(output, notification);
|
|
42
|
+
}
|
|
43
|
+
for (const response of responses) {
|
|
44
|
+
writeMessage(output, response);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const writeMessage = (output, message) => {
|
|
49
|
+
output.write(`${JSON.stringify(message)}\n`);
|
|
50
|
+
};
|
|
51
|
+
const isJsonRpcRequest = (value) => {
|
|
52
|
+
if (!isRecord(value)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (value.jsonrpc !== "2.0") {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value.method !== "string") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return hasId(value);
|
|
62
|
+
};
|
|
63
|
+
const hasId = (value) => {
|
|
64
|
+
if (!isRecord(value)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return (typeof value.id === "string" ||
|
|
68
|
+
typeof value.id === "number" ||
|
|
69
|
+
value.id === null);
|
|
70
|
+
};
|
|
71
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const createAdapterState = () => ({
|
|
2
|
+
nextSessionId: 1,
|
|
3
|
+
sessions: new Map(),
|
|
4
|
+
});
|
|
5
|
+
export const generateSessionId = (state) => {
|
|
6
|
+
const id = `session-${state.nextSessionId}`;
|
|
7
|
+
state.nextSessionId += 1;
|
|
8
|
+
return id;
|
|
9
|
+
};
|
|
10
|
+
export const ensureSession = (state, sessionId) => {
|
|
11
|
+
if (!state.sessions.has(sessionId)) {
|
|
12
|
+
state.sessions.set(sessionId, { id: sessionId });
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const coreVersion = "0.0.1";
|
package/dist/index.js
ADDED
package/dist/main.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "noema-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"noema-cli": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"exports": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"test": "npm run build && node --test ./tests/*.test.js",
|
|
16
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {}
|
|
19
|
+
}
|