@stevezhou/sisu 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +12 -0
- package/README.md +3 -2
- package/dist/commands.js +36 -84
- package/dist/main.js +7 -5
- package/dist/pager/app.js +3 -3
- package/dist/pager/input.js +43 -20
- package/dist/pager/model.js +94 -25
- package/dist/runtime/adapter.js +164 -0
- package/dist/runtime/index.js +33 -0
- package/dist/runtime/launch.js +94 -0
- package/dist/runtime/loop.js +91 -0
- package/dist/runtime/models.js +56 -0
- package/dist/runtime/sessions.js +65 -0
- package/dist/runtime/suite.js +64 -0
- package/dist/runtime/tools.js +256 -0
- package/dist/runtime/transport.js +93 -0
- package/dist/runtime/types.js +2 -0
- package/dist/tui.js +17 -3
- package/package.json +4 -3
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LOCAL_TOOL_NAMES = void 0;
|
|
7
|
+
exports.localToolDefinitions = localToolDefinitions;
|
|
8
|
+
exports.resolveWorkspaceRoot = resolveWorkspaceRoot;
|
|
9
|
+
exports.resolveInWorkspace = resolveInWorkspace;
|
|
10
|
+
exports.dispatchLocalTool = dispatchLocalTool;
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const MAX_READ_BYTES = 256 * 1024;
|
|
15
|
+
const MAX_RESULT = 40_000;
|
|
16
|
+
exports.LOCAL_TOOL_NAMES = ['read_file', 'search_replace', 'grep', 'bash'];
|
|
17
|
+
function localToolDefinitions() {
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
type: 'function',
|
|
21
|
+
function: {
|
|
22
|
+
name: 'read_file',
|
|
23
|
+
description: 'Read a file from the local workspace. Path may be relative or absolute.',
|
|
24
|
+
parameters: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
target_file: { type: 'string' },
|
|
28
|
+
path: { type: 'string' },
|
|
29
|
+
offset: { type: 'integer' },
|
|
30
|
+
limit: { type: 'integer' },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'function',
|
|
37
|
+
function: {
|
|
38
|
+
name: 'search_replace',
|
|
39
|
+
description: 'Edit a file. Empty old_string creates or overwrites. replace_all replaces every match.',
|
|
40
|
+
parameters: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
file_path: { type: 'string' },
|
|
44
|
+
old_string: { type: 'string' },
|
|
45
|
+
new_string: { type: 'string' },
|
|
46
|
+
replace_all: { type: 'boolean' },
|
|
47
|
+
},
|
|
48
|
+
required: ['file_path', 'old_string', 'new_string'],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'function',
|
|
54
|
+
function: {
|
|
55
|
+
name: 'grep',
|
|
56
|
+
description: 'Search file contents in the workspace (ripgrep if present).',
|
|
57
|
+
parameters: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
pattern: { type: 'string' },
|
|
61
|
+
path: { type: 'string' },
|
|
62
|
+
glob: { type: 'string' },
|
|
63
|
+
},
|
|
64
|
+
required: ['pattern'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'function',
|
|
70
|
+
function: {
|
|
71
|
+
name: 'bash',
|
|
72
|
+
description: 'Run a shell command against the workspace cwd.',
|
|
73
|
+
parameters: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
command: { type: 'string' },
|
|
77
|
+
cmd: { type: 'string' },
|
|
78
|
+
},
|
|
79
|
+
required: ['command'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
function resolveWorkspaceRoot(cwd) {
|
|
86
|
+
const root = path_1.default.resolve(cwd || process.cwd());
|
|
87
|
+
if (!fs_1.default.existsSync(root) || !fs_1.default.statSync(root).isDirectory()) {
|
|
88
|
+
throw new Error(`workspace is not a directory: ${root}`);
|
|
89
|
+
}
|
|
90
|
+
return root;
|
|
91
|
+
}
|
|
92
|
+
function resolveInWorkspace(root, requested) {
|
|
93
|
+
const trimmed = (requested || '').trim() || '.';
|
|
94
|
+
const absolute = path_1.default.isAbsolute(trimmed) ? path_1.default.resolve(trimmed) : path_1.default.resolve(root, trimmed);
|
|
95
|
+
const rel = path_1.default.relative(root, absolute);
|
|
96
|
+
if (rel.startsWith('..') || path_1.default.isAbsolute(rel)) {
|
|
97
|
+
throw new Error(`path escapes workspace: ${requested}`);
|
|
98
|
+
}
|
|
99
|
+
return absolute;
|
|
100
|
+
}
|
|
101
|
+
function clip(text) {
|
|
102
|
+
if (text.length <= MAX_RESULT)
|
|
103
|
+
return text;
|
|
104
|
+
return `${text.slice(0, MAX_RESULT)}\n…truncated`;
|
|
105
|
+
}
|
|
106
|
+
function str(input, ...keys) {
|
|
107
|
+
for (const key of keys) {
|
|
108
|
+
const value = input[key];
|
|
109
|
+
if (typeof value === 'string' && value)
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
return '';
|
|
113
|
+
}
|
|
114
|
+
function readFileTool(root, input) {
|
|
115
|
+
const target = str(input, 'target_file', 'path', 'file_path');
|
|
116
|
+
if (!target)
|
|
117
|
+
throw new Error('read_file requires target_file or path');
|
|
118
|
+
const file = resolveInWorkspace(root, target);
|
|
119
|
+
const raw = fs_1.default.readFileSync(file);
|
|
120
|
+
const start = Math.max(0, Number(input.offset) || 0);
|
|
121
|
+
const limit = Number(input.limit);
|
|
122
|
+
const text = raw.subarray(0, MAX_READ_BYTES).toString('utf8');
|
|
123
|
+
const lines = text.split('\n');
|
|
124
|
+
const slice = Number.isFinite(limit) && limit > 0 ? lines.slice(start, start + limit) : lines.slice(start);
|
|
125
|
+
return slice.join('\n');
|
|
126
|
+
}
|
|
127
|
+
function searchReplaceTool(root, input) {
|
|
128
|
+
const filePath = str(input, 'file_path', 'path', 'target_file');
|
|
129
|
+
if (!filePath)
|
|
130
|
+
throw new Error('search_replace requires file_path');
|
|
131
|
+
const oldString = typeof input.old_string === 'string' ? input.old_string : '';
|
|
132
|
+
const newString = typeof input.new_string === 'string' ? input.new_string : String(input.contents ?? input.content ?? '');
|
|
133
|
+
const file = resolveInWorkspace(root, filePath);
|
|
134
|
+
if (!oldString) {
|
|
135
|
+
fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
|
|
136
|
+
fs_1.default.writeFileSync(file, newString, 'utf8');
|
|
137
|
+
return `The file ${filePath} has been created.`;
|
|
138
|
+
}
|
|
139
|
+
if (!fs_1.default.existsSync(file))
|
|
140
|
+
throw new Error(`file not found: ${filePath}`);
|
|
141
|
+
const before = fs_1.default.readFileSync(file, 'utf8');
|
|
142
|
+
if (!before.includes(oldString))
|
|
143
|
+
throw new Error(`old_string not found in ${filePath}`);
|
|
144
|
+
const replaceAll = Boolean(input.replace_all);
|
|
145
|
+
const after = replaceAll ? before.split(oldString).join(newString) : before.replace(oldString, newString);
|
|
146
|
+
fs_1.default.writeFileSync(file, after, 'utf8');
|
|
147
|
+
return `The file ${filePath} has been updated.`;
|
|
148
|
+
}
|
|
149
|
+
function walkFiles(dir, glob, acc) {
|
|
150
|
+
let entries;
|
|
151
|
+
try {
|
|
152
|
+
entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
if (entry.name === '.git' || entry.name === 'node_modules')
|
|
159
|
+
continue;
|
|
160
|
+
const full = path_1.default.join(dir, entry.name);
|
|
161
|
+
if (entry.isDirectory())
|
|
162
|
+
walkFiles(full, glob, acc);
|
|
163
|
+
else if (!glob || matchGlob(entry.name, glob))
|
|
164
|
+
acc.push(full);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function matchGlob(name, glob) {
|
|
168
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.');
|
|
169
|
+
return new RegExp(`^${escaped}$`).test(name);
|
|
170
|
+
}
|
|
171
|
+
function grepTool(root, input) {
|
|
172
|
+
const pattern = str(input, 'pattern');
|
|
173
|
+
if (!pattern)
|
|
174
|
+
throw new Error('grep requires pattern');
|
|
175
|
+
const searchRoot = resolveInWorkspace(root, str(input, 'path') || '.');
|
|
176
|
+
const glob = str(input, 'glob') || undefined;
|
|
177
|
+
const rg = (0, child_process_1.spawnSync)('rg', ['--line-number', '--no-heading', '--color', 'never', pattern, searchRoot], {
|
|
178
|
+
encoding: 'utf8',
|
|
179
|
+
timeout: 15_000,
|
|
180
|
+
maxBuffer: MAX_RESULT,
|
|
181
|
+
});
|
|
182
|
+
if (rg.error || rg.status === 127) {
|
|
183
|
+
const files = [];
|
|
184
|
+
if (fs_1.default.existsSync(searchRoot) && fs_1.default.statSync(searchRoot).isFile())
|
|
185
|
+
files.push(searchRoot);
|
|
186
|
+
else
|
|
187
|
+
walkFiles(searchRoot, glob, files);
|
|
188
|
+
const re = new RegExp(pattern);
|
|
189
|
+
const hits = [];
|
|
190
|
+
for (const file of files) {
|
|
191
|
+
let text = '';
|
|
192
|
+
try {
|
|
193
|
+
text = fs_1.default.readFileSync(file, 'utf8');
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const lines = text.split('\n');
|
|
199
|
+
lines.forEach((line, index) => {
|
|
200
|
+
if (re.test(line))
|
|
201
|
+
hits.push(`${file}:${index + 1}:${line}`);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return hits.length ? hits.join('\n') : 'no matches';
|
|
205
|
+
}
|
|
206
|
+
if (rg.status === 1)
|
|
207
|
+
return 'no matches';
|
|
208
|
+
if (rg.status !== 0)
|
|
209
|
+
throw new Error(rg.stderr || `rg failed (${rg.status})`);
|
|
210
|
+
return (rg.stdout || '').trim() || 'no matches';
|
|
211
|
+
}
|
|
212
|
+
function bashTool(root, input) {
|
|
213
|
+
const command = str(input, 'command', 'cmd');
|
|
214
|
+
if (!command)
|
|
215
|
+
throw new Error('bash requires command');
|
|
216
|
+
const result = (0, child_process_1.spawnSync)(command, {
|
|
217
|
+
cwd: root,
|
|
218
|
+
encoding: 'utf8',
|
|
219
|
+
shell: true,
|
|
220
|
+
timeout: 30_000,
|
|
221
|
+
maxBuffer: MAX_RESULT,
|
|
222
|
+
env: { ...process.env, SISU_WORKSPACE: root },
|
|
223
|
+
});
|
|
224
|
+
const out = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
225
|
+
if (result.error)
|
|
226
|
+
throw new Error(result.error.message);
|
|
227
|
+
if (result.status !== 0) {
|
|
228
|
+
return clip(out || `exit ${result.status}`);
|
|
229
|
+
}
|
|
230
|
+
return clip(out || '(ok)');
|
|
231
|
+
}
|
|
232
|
+
function dispatchLocalTool(root, call) {
|
|
233
|
+
try {
|
|
234
|
+
const name = call.name;
|
|
235
|
+
let content = '';
|
|
236
|
+
if (name === 'read_file')
|
|
237
|
+
content = readFileTool(root, call.arguments);
|
|
238
|
+
else if (name === 'search_replace')
|
|
239
|
+
content = searchReplaceTool(root, call.arguments);
|
|
240
|
+
else if (name === 'grep')
|
|
241
|
+
content = grepTool(root, call.arguments);
|
|
242
|
+
else if (name === 'bash' || name === 'run_terminal_cmd')
|
|
243
|
+
content = bashTool(root, call.arguments);
|
|
244
|
+
else
|
|
245
|
+
throw new Error(`unknown tool: ${name}`);
|
|
246
|
+
return { id: call.id, name, content: clip(content), ok: true };
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
return {
|
|
250
|
+
id: call.id,
|
|
251
|
+
name: call.name,
|
|
252
|
+
content: error instanceof Error ? error.message : String(error),
|
|
253
|
+
ok: false,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLocalRuntimeTransport = createLocalRuntimeTransport;
|
|
4
|
+
exports.execLocalTurn = execLocalTurn;
|
|
5
|
+
const store_1 = require("../store");
|
|
6
|
+
const adapter_1 = require("./adapter");
|
|
7
|
+
const loop_1 = require("./loop");
|
|
8
|
+
const models_1 = require("./models");
|
|
9
|
+
const sessions_1 = require("./sessions");
|
|
10
|
+
const tools_1 = require("./tools");
|
|
11
|
+
function createLocalRuntimeTransport(http, options = {}) {
|
|
12
|
+
return {
|
|
13
|
+
async *send(prompt, sendOptions = {}) {
|
|
14
|
+
const cwd = (0, tools_1.resolveWorkspaceRoot)(options.cwd);
|
|
15
|
+
let conversationId = sendOptions.conversationId || (!sendOptions.newConversation ? (0, store_1.readSession)().last_conversation_id : '') || '';
|
|
16
|
+
let existing = conversationId ? (0, sessions_1.loadLocalSession)(conversationId) : null;
|
|
17
|
+
if (!existing || sendOptions.newConversation) {
|
|
18
|
+
existing = (0, sessions_1.createLocalSession)(prompt.trim().slice(0, 50), cwd, (0, store_1.readSession)().last_model);
|
|
19
|
+
conversationId = existing.id;
|
|
20
|
+
}
|
|
21
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: conversationId });
|
|
22
|
+
const client = options.modelClient || cloudClient(http, options.client || 'tui');
|
|
23
|
+
const model = options.modelClient
|
|
24
|
+
? existing.model || (0, store_1.readSession)().last_model || 'stub'
|
|
25
|
+
: await (0, models_1.resolveRuntimeModel)(http, { explicit: existing.model || (0, store_1.readSession)().last_model });
|
|
26
|
+
const gen = (0, loop_1.runLocalTurn)({
|
|
27
|
+
prompt,
|
|
28
|
+
cwd,
|
|
29
|
+
model,
|
|
30
|
+
client,
|
|
31
|
+
messages: existing.messages,
|
|
32
|
+
conversationId,
|
|
33
|
+
});
|
|
34
|
+
let step = await gen.next();
|
|
35
|
+
while (!step.done) {
|
|
36
|
+
const event = step.value;
|
|
37
|
+
yield event;
|
|
38
|
+
step = await gen.next();
|
|
39
|
+
}
|
|
40
|
+
const result = step.value;
|
|
41
|
+
existing.messages.push({ role: 'user', content: prompt });
|
|
42
|
+
if (result.text)
|
|
43
|
+
existing.messages.push({ role: 'assistant', content: result.text });
|
|
44
|
+
(0, sessions_1.saveLocalSession)(existing);
|
|
45
|
+
return { conversationId: result.conversationId };
|
|
46
|
+
},
|
|
47
|
+
async listConversations() {
|
|
48
|
+
return (0, sessions_1.listLocalSessions)().map((row) => ({ id: row.id, title: row.title, client: 'tui' }));
|
|
49
|
+
},
|
|
50
|
+
async getConversation(id) {
|
|
51
|
+
const row = (0, sessions_1.loadLocalSession)(id);
|
|
52
|
+
if (!row)
|
|
53
|
+
throw new Error('conversation not found');
|
|
54
|
+
const messages = row.messages.map((message, index) => ({
|
|
55
|
+
id: `${row.id}-${index}`,
|
|
56
|
+
role: message.role === 'tool' ? 'assistant' : message.role,
|
|
57
|
+
content: message.content,
|
|
58
|
+
...(message.role === 'tool' ? { message_type: 'tool_result' } : {}),
|
|
59
|
+
}));
|
|
60
|
+
return { id: row.id, title: row.title, messages };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function execLocalTurn(prompt, options = {}) {
|
|
65
|
+
const cwd = (0, tools_1.resolveWorkspaceRoot)(options.cwd);
|
|
66
|
+
let conversationId = options.conversationId || (!options.newConversation ? (0, store_1.readSession)().last_conversation_id : '') || '';
|
|
67
|
+
let existing = conversationId ? (0, sessions_1.loadLocalSession)(conversationId) : null;
|
|
68
|
+
if (!existing) {
|
|
69
|
+
existing = (0, sessions_1.createLocalSession)(prompt.trim().slice(0, 50), cwd, options.model);
|
|
70
|
+
conversationId = existing.id;
|
|
71
|
+
}
|
|
72
|
+
const client = options.modelClient || (options.http ? cloudClient(options.http, options.client || 'cli') : undefined);
|
|
73
|
+
if (!client)
|
|
74
|
+
throw new Error('model client required');
|
|
75
|
+
const result = await (0, loop_1.collectLocalTurn)({
|
|
76
|
+
prompt,
|
|
77
|
+
cwd,
|
|
78
|
+
model: options.model || existing.model || (0, store_1.readSession)().last_model || 'stub',
|
|
79
|
+
client,
|
|
80
|
+
messages: existing.messages,
|
|
81
|
+
conversationId,
|
|
82
|
+
});
|
|
83
|
+
existing.messages.push({ role: 'user', content: prompt });
|
|
84
|
+
if (result.text)
|
|
85
|
+
existing.messages.push({ role: 'assistant', content: result.text });
|
|
86
|
+
(0, sessions_1.saveLocalSession)(existing);
|
|
87
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: result.conversationId, last_model: options.model || (0, store_1.readSession)().last_model });
|
|
88
|
+
return { conversationId: result.conversationId, text: result.text, toolResults: result.toolResults };
|
|
89
|
+
}
|
|
90
|
+
function cloudClient(http, client) {
|
|
91
|
+
const auth = (0, store_1.requireAuth)();
|
|
92
|
+
return (0, adapter_1.createSisuCloudModel)(http, { apiBase: auth.api_base, token: auth.token, client });
|
|
93
|
+
}
|
package/dist/tui.js
CHANGED
|
@@ -17,7 +17,9 @@ const mobius_1 = require("./mobius");
|
|
|
17
17
|
const app_1 = require("./pager/app");
|
|
18
18
|
const stdio_1 = require("./pager/stdio");
|
|
19
19
|
const store_1 = require("./store");
|
|
20
|
-
const
|
|
20
|
+
const launch_1 = require("./runtime/launch");
|
|
21
|
+
const transport_1 = require("./runtime/transport");
|
|
22
|
+
const child_process_1 = require("child_process");
|
|
21
23
|
function shouldAnimateSplash(env = process.env, tty = Boolean(process.stdout.isTTY)) {
|
|
22
24
|
if (env.SISU_TUI_STATIC === '1')
|
|
23
25
|
return false;
|
|
@@ -178,7 +180,7 @@ function tuiHelp() {
|
|
|
178
180
|
'/export write the thread to a markdown file',
|
|
179
181
|
'/status account and quota',
|
|
180
182
|
'/ls local workspace files',
|
|
181
|
-
'/history saved
|
|
183
|
+
'/history saved local sessions',
|
|
182
184
|
'/open <id> continue a saved conversation',
|
|
183
185
|
'/new start a new conversation',
|
|
184
186
|
'/training on|off allow or refuse training use of new turns',
|
|
@@ -222,9 +224,21 @@ async function runTui(io, deps = {}) {
|
|
|
222
224
|
},
|
|
223
225
|
}, http);
|
|
224
226
|
};
|
|
227
|
+
if (usePager && !deps.pager) {
|
|
228
|
+
const grokBin = (0, launch_1.findGrokBuildBinary)();
|
|
229
|
+
if (grokBin && process.stdout.isTTY) {
|
|
230
|
+
(0, launch_1.writeSisuGrokConfig)();
|
|
231
|
+
io.close?.();
|
|
232
|
+
const child = (0, child_process_1.spawn)(grokBin, [], { stdio: 'inherit', env: (0, launch_1.sisuGrokBuildEnv)(), cwd: process.cwd() });
|
|
233
|
+
return await new Promise((resolve) => {
|
|
234
|
+
child.on('exit', (code) => resolve(code ?? 1));
|
|
235
|
+
child.on('error', () => resolve(1));
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
225
239
|
if (usePager) {
|
|
226
240
|
io.close?.();
|
|
227
|
-
const transport = (0, transport_1.
|
|
241
|
+
const transport = (0, transport_1.createLocalRuntimeTransport)(http, { client: 'tui' });
|
|
228
242
|
return await (deps.pager ?? app_1.runPager)((0, stdio_1.stdioPagerIo)(), transport, {
|
|
229
243
|
columns,
|
|
230
244
|
email: account?.email,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevezhou/sisu",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SiSu CLI —
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "SiSu CLI — local grok-build runtime, SiSu cloud models",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://www.sisu.chat",
|
|
7
7
|
"bugs": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"main": "dist/main.js",
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
|
-
"README.md"
|
|
25
|
+
"README.md",
|
|
26
|
+
"NOTICE"
|
|
26
27
|
],
|
|
27
28
|
"publishConfig": {
|
|
28
29
|
"access": "public"
|