noiton 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -12
- package/package.json +2 -7
- package/src/commands/change.js +2 -2
- package/src/commands/init.js +3 -2
- package/src/commands/refresh.js +2 -2
- package/src/commands/remove.js +2 -2
- package/src/commands/status.js +2 -2
- package/src/index.js +0 -5
- package/src/lib/agents/hermes.js +20 -1
- package/src/lib/agents/index.js +42 -12
- package/src/lib/agents/opencode.js +19 -1
- package/src/lib/detect.js +47 -107
- package/src/lib/install.js +0 -2
- package/src/octopus.js +62 -212
- package/src/ui.js +2 -23
- package/src/lib/agents/aider.js +0 -141
- package/src/lib/agents/cline.js +0 -128
- package/src/lib/agents/kilo.js +0 -140
- package/src/lib/agents/kimi.js +0 -149
- package/src/lib/agents/openclaw.js +0 -155
package/src/lib/agents/kilo.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Kilo Code Agent Adapter
|
|
3
|
-
*
|
|
4
|
-
* Config: ~/.config/kilo/kilo.jsonc (JSONC)
|
|
5
|
-
* Format (under `provider.openai-compatible`):
|
|
6
|
-
* model: openai-compatible/<model-id>
|
|
7
|
-
* provider.openai-compatible.options.apiKey
|
|
8
|
-
* provider.openai-compatible.options.baseURL
|
|
9
|
-
* provider.openai-compatible.models.<id>.limit.context/output
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
13
|
-
import { join, dirname } from "node:path";
|
|
14
|
-
import { homedir } from "node:os";
|
|
15
|
-
import JSON5 from "json5";
|
|
16
|
-
import { BASE_URL } from "../../constants.js";
|
|
17
|
-
|
|
18
|
-
const HOME = homedir();
|
|
19
|
-
const XDG = process.env.XDG_CONFIG_HOME || join(HOME, ".config");
|
|
20
|
-
const CONFIG_PATH = join(XDG, "kilo", "kilo.jsonc");
|
|
21
|
-
const PROVIDER_ID = "openai-compatible";
|
|
22
|
-
|
|
23
|
-
function readConfig() {
|
|
24
|
-
if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
|
|
25
|
-
try {
|
|
26
|
-
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
27
|
-
// Strip comments (JSONC)
|
|
28
|
-
const cleaned = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
29
|
-
return { config: JSON5.parse(cleaned), path: CONFIG_PATH };
|
|
30
|
-
} catch {
|
|
31
|
-
return { config: {}, path: CONFIG_PATH };
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function writeConfig(config) {
|
|
36
|
-
if (!existsSync(dirname(CONFIG_PATH))) {
|
|
37
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildModels(models) {
|
|
43
|
-
const obj = {};
|
|
44
|
-
for (const m of models) {
|
|
45
|
-
if (!m.id) continue;
|
|
46
|
-
obj[m.id] = {
|
|
47
|
-
name: prettifyName(m.id),
|
|
48
|
-
tool_call: true,
|
|
49
|
-
limit: { context: m.context_window || 32768, output: 4096 },
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return obj;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function prettifyName(id) {
|
|
56
|
-
return id
|
|
57
|
-
.replace(/[@/]/g, " ")
|
|
58
|
-
.split(/[-_]/)
|
|
59
|
-
.filter(Boolean)
|
|
60
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
61
|
-
.join(" ");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ============= ADAPTER INTERFACE =============
|
|
65
|
-
|
|
66
|
-
export const kiloAgent = {
|
|
67
|
-
id: "kilo",
|
|
68
|
-
name: "Kilo Code",
|
|
69
|
-
supportsMultipleModels: true,
|
|
70
|
-
|
|
71
|
-
detect() {
|
|
72
|
-
return existsSync(CONFIG_PATH);
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
isConfigured() {
|
|
76
|
-
const { config } = readConfig();
|
|
77
|
-
return Boolean(config?.provider?.[PROVIDER_ID]?.options?.baseURL === BASE_URL);
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
getStatus() {
|
|
81
|
-
const { config, path } = readConfig();
|
|
82
|
-
const provider = config?.provider?.[PROVIDER_ID];
|
|
83
|
-
if (!provider) return { configured: false, path, models: [], apiKey: null };
|
|
84
|
-
const apiKey = provider.options?.apiKey;
|
|
85
|
-
const models = provider.models ? Object.keys(provider.models) : [];
|
|
86
|
-
return {
|
|
87
|
-
configured: provider.options?.baseURL === BASE_URL,
|
|
88
|
-
path,
|
|
89
|
-
models,
|
|
90
|
-
apiKey: apiKey ? (apiKey.startsWith("{env:") ? "(env var)" : `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}`) : null,
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
configure({ apiKey, models, primaryModel }) {
|
|
95
|
-
const { config } = readConfig();
|
|
96
|
-
config.model = `${PROVIDER_ID}/${primaryModel || models[0]?.id}`;
|
|
97
|
-
config.provider = config.provider || {};
|
|
98
|
-
config.provider[PROVIDER_ID] = {
|
|
99
|
-
options: {
|
|
100
|
-
apiKey,
|
|
101
|
-
baseURL: `${BASE_URL}/v1`,
|
|
102
|
-
},
|
|
103
|
-
models: buildModels(models),
|
|
104
|
-
};
|
|
105
|
-
writeConfig(config);
|
|
106
|
-
return { path: CONFIG_PATH, modelsCount: models.length };
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
changeKey(apiKey) {
|
|
110
|
-
const { config } = readConfig();
|
|
111
|
-
if (!config?.provider?.[PROVIDER_ID]) {
|
|
112
|
-
throw new Error("NOITON não está configurado no Kilo Code");
|
|
113
|
-
}
|
|
114
|
-
config.provider[PROVIDER_ID].options.apiKey = apiKey;
|
|
115
|
-
writeConfig(config);
|
|
116
|
-
return { path: CONFIG_PATH };
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
refresh(models) {
|
|
120
|
-
const { config } = readConfig();
|
|
121
|
-
if (!config?.provider?.[PROVIDER_ID]) {
|
|
122
|
-
throw new Error("NOITON não está configurado no Kilo Code");
|
|
123
|
-
}
|
|
124
|
-
config.provider[PROVIDER_ID].models = buildModels(models);
|
|
125
|
-
writeConfig(config);
|
|
126
|
-
return { path: CONFIG_PATH, modelsCount: models.length };
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
remove() {
|
|
130
|
-
const { config } = readConfig();
|
|
131
|
-
let removed = false;
|
|
132
|
-
if (config?.provider?.[PROVIDER_ID]) {
|
|
133
|
-
delete config.provider[PROVIDER_ID];
|
|
134
|
-
delete config.model;
|
|
135
|
-
removed = true;
|
|
136
|
-
writeConfig(config);
|
|
137
|
-
}
|
|
138
|
-
return { path: CONFIG_PATH, removed };
|
|
139
|
-
},
|
|
140
|
-
};
|
package/src/lib/agents/kimi.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Kimi Code Agent Adapter
|
|
3
|
-
*
|
|
4
|
-
* Config: ~/.kimi-code/config.toml (TOML)
|
|
5
|
-
* Format:
|
|
6
|
-
* [providers.noiton]
|
|
7
|
-
* type = "openai"
|
|
8
|
-
* base_url = "<BASE_URL>/v1"
|
|
9
|
-
* api_key = "<key>"
|
|
10
|
-
*
|
|
11
|
-
* [models."<id>"]
|
|
12
|
-
* provider = "noiton"
|
|
13
|
-
* model = "<id>"
|
|
14
|
-
* max_context_size = <ctx>
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
18
|
-
import { join, dirname } from "node:path";
|
|
19
|
-
import { homedir } from "node:os";
|
|
20
|
-
import * as TOML from "@iarna/toml";
|
|
21
|
-
import { BASE_URL, PROVIDER_ID } from "../../constants.js";
|
|
22
|
-
|
|
23
|
-
const HOME = homedir();
|
|
24
|
-
const CONFIG_PATH = join(HOME, ".kimi-code", "config.toml");
|
|
25
|
-
|
|
26
|
-
function readConfig() {
|
|
27
|
-
if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
|
|
28
|
-
try {
|
|
29
|
-
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
30
|
-
return { config: TOML.parse(raw), path: CONFIG_PATH };
|
|
31
|
-
} catch {
|
|
32
|
-
return { config: {}, path: CONFIG_PATH };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function writeConfig(config) {
|
|
37
|
-
if (!existsSync(dirname(CONFIG_PATH))) {
|
|
38
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
writeFileSync(CONFIG_PATH, TOML.stringify(config), "utf8");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ============= ADAPTER INTERFACE =============
|
|
44
|
-
|
|
45
|
-
export const kimiAgent = {
|
|
46
|
-
id: "kimi",
|
|
47
|
-
name: "Kimi Code",
|
|
48
|
-
supportsMultipleModels: false,
|
|
49
|
-
|
|
50
|
-
detect() {
|
|
51
|
-
return existsSync(CONFIG_PATH);
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
isConfigured() {
|
|
55
|
-
const { config } = readConfig();
|
|
56
|
-
return Boolean(config?.providers?.[PROVIDER_ID]);
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
getStatus() {
|
|
60
|
-
const { config, path } = readConfig();
|
|
61
|
-
const provider = config?.providers?.[PROVIDER_ID];
|
|
62
|
-
if (!provider) return { configured: false, path, models: [], apiKey: null };
|
|
63
|
-
const apiKey = provider.api_key;
|
|
64
|
-
const models = config.models
|
|
65
|
-
? Object.entries(config.models)
|
|
66
|
-
.filter(([, v]) => v.provider === PROVIDER_ID)
|
|
67
|
-
.map(([k]) => k)
|
|
68
|
-
: [];
|
|
69
|
-
return {
|
|
70
|
-
configured: true,
|
|
71
|
-
path,
|
|
72
|
-
models,
|
|
73
|
-
apiKey: apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : null,
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
configure({ apiKey, models, primaryModel }) {
|
|
78
|
-
const { config } = readConfig();
|
|
79
|
-
config.providers = config.providers || {};
|
|
80
|
-
config.providers[PROVIDER_ID] = {
|
|
81
|
-
type: "openai",
|
|
82
|
-
base_url: `${BASE_URL}/v1`,
|
|
83
|
-
api_key: apiKey,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
config.models = config.models || {};
|
|
87
|
-
const modelId = primaryModel || models[0]?.id || "glm-5.2";
|
|
88
|
-
config.models[modelId] = {
|
|
89
|
-
provider: PROVIDER_ID,
|
|
90
|
-
model: modelId,
|
|
91
|
-
max_context_size: models[0]?.context_window || 32768,
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
writeConfig(config);
|
|
95
|
-
return { path: CONFIG_PATH, modelsCount: 1 };
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
changeKey(apiKey) {
|
|
99
|
-
const { config } = readConfig();
|
|
100
|
-
if (!config?.providers?.[PROVIDER_ID]) {
|
|
101
|
-
throw new Error("NOITON não está configurado no Kimi Code");
|
|
102
|
-
}
|
|
103
|
-
config.providers[PROVIDER_ID].api_key = apiKey;
|
|
104
|
-
writeConfig(config);
|
|
105
|
-
return { path: CONFIG_PATH };
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
refresh(models) {
|
|
109
|
-
const { config } = readConfig();
|
|
110
|
-
if (!config?.providers?.[PROVIDER_ID]) {
|
|
111
|
-
throw new Error("NOITON não está configurado no Kimi Code");
|
|
112
|
-
}
|
|
113
|
-
// Update context size if model exists
|
|
114
|
-
const modelId = Object.keys(config.models || {}).find(
|
|
115
|
-
(k) => config.models[k]?.provider === PROVIDER_ID
|
|
116
|
-
);
|
|
117
|
-
if (modelId && models[0]?.context_window) {
|
|
118
|
-
config.models[modelId].max_context_size = models[0].context_window;
|
|
119
|
-
}
|
|
120
|
-
writeConfig(config);
|
|
121
|
-
return { path: CONFIG_PATH, modelsCount: 1 };
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
remove() {
|
|
125
|
-
const { config } = readConfig();
|
|
126
|
-
let removed = false;
|
|
127
|
-
if (config?.providers?.[PROVIDER_ID]) {
|
|
128
|
-
delete config.providers[PROVIDER_ID];
|
|
129
|
-
removed = true;
|
|
130
|
-
}
|
|
131
|
-
if (config?.models) {
|
|
132
|
-
for (const [k, v] of Object.entries(config.models)) {
|
|
133
|
-
if (v.provider === PROVIDER_ID) {
|
|
134
|
-
delete config.models[k];
|
|
135
|
-
removed = true;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Clean up empty top-level tables
|
|
140
|
-
if (config.providers && Object.keys(config.providers).length === 0) {
|
|
141
|
-
delete config.providers;
|
|
142
|
-
}
|
|
143
|
-
if (config.models && Object.keys(config.models).length === 0) {
|
|
144
|
-
delete config.models;
|
|
145
|
-
}
|
|
146
|
-
if (removed) writeConfig(config);
|
|
147
|
-
return { path: CONFIG_PATH, removed };
|
|
148
|
-
},
|
|
149
|
-
};
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenClaw Agent Adapter
|
|
3
|
-
*
|
|
4
|
-
* Config: ~/.openclaw/openclaw.json (JSON5)
|
|
5
|
-
* Format:
|
|
6
|
-
* models.providers.noiton = {
|
|
7
|
-
* baseUrl, apiKey, api: "openai-completions", models: [...]
|
|
8
|
-
* }
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
12
|
-
import { join, dirname } from "node:path";
|
|
13
|
-
import { homedir } from "node:os";
|
|
14
|
-
import JSON5 from "json5";
|
|
15
|
-
import { BASE_URL, PROVIDER_ID, PROVIDER_NAME } from "../../constants.js";
|
|
16
|
-
|
|
17
|
-
const HOME = homedir();
|
|
18
|
-
const CONFIG_PATH = join(HOME, ".openclaw", "openclaw.json");
|
|
19
|
-
|
|
20
|
-
function readConfig() {
|
|
21
|
-
if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
|
|
22
|
-
try {
|
|
23
|
-
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
24
|
-
return { config: JSON5.parse(raw), path: CONFIG_PATH };
|
|
25
|
-
} catch {
|
|
26
|
-
return { config: {}, path: CONFIG_PATH };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function writeConfig(config) {
|
|
31
|
-
if (!existsSync(dirname(CONFIG_PATH))) {
|
|
32
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
33
|
-
}
|
|
34
|
-
// Write as standard JSON (OpenClaw accepts JSON5 which is a superset)
|
|
35
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ============= ADAPTER INTERFACE =============
|
|
39
|
-
|
|
40
|
-
export const openclawAgent = {
|
|
41
|
-
id: "openclaw",
|
|
42
|
-
name: "OpenClaw",
|
|
43
|
-
supportsMultipleModels: true,
|
|
44
|
-
|
|
45
|
-
detect() {
|
|
46
|
-
return existsSync(CONFIG_PATH);
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
isConfigured() {
|
|
50
|
-
const { config } = readConfig();
|
|
51
|
-
return Boolean(config?.models?.providers?.[PROVIDER_ID]);
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
getStatus() {
|
|
55
|
-
const { config, path } = readConfig();
|
|
56
|
-
const provider = config?.models?.providers?.[PROVIDER_ID];
|
|
57
|
-
if (!provider) return { configured: false, path, models: [], apiKey: null };
|
|
58
|
-
const models = (provider.models || []).map((m) => m.id);
|
|
59
|
-
return {
|
|
60
|
-
configured: true,
|
|
61
|
-
path,
|
|
62
|
-
models,
|
|
63
|
-
apiKey: provider.apiKey,
|
|
64
|
-
};
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
configure({ apiKey, models, primaryModel }) {
|
|
68
|
-
const { config } = readConfig();
|
|
69
|
-
config.models = config.models || {};
|
|
70
|
-
config.models.mode = config.models.mode || "merge";
|
|
71
|
-
config.models.providers = config.models.providers || {};
|
|
72
|
-
|
|
73
|
-
config.models.providers[PROVIDER_ID] = {
|
|
74
|
-
baseUrl: `${BASE_URL}/v1`,
|
|
75
|
-
apiKey: `\${NOITON_API_KEY}`,
|
|
76
|
-
api: "openai-completions",
|
|
77
|
-
models: models.map((m) => ({
|
|
78
|
-
id: m.id,
|
|
79
|
-
name: prettifyName(m.id),
|
|
80
|
-
reasoning: false,
|
|
81
|
-
input: ["text"],
|
|
82
|
-
contextWindow: m.context_window || 32768,
|
|
83
|
-
maxTokens: 4096,
|
|
84
|
-
})),
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Set env for apiKey substitution
|
|
88
|
-
config.env = config.env || {};
|
|
89
|
-
config.env.NOITON_API_KEY = apiKey;
|
|
90
|
-
|
|
91
|
-
// Set default model if missing
|
|
92
|
-
if (!config.agents) config.agents = {};
|
|
93
|
-
if (!config.agents.defaults) config.agents.defaults = {};
|
|
94
|
-
if (!config.agents.defaults.model) {
|
|
95
|
-
config.agents.defaults.model = {
|
|
96
|
-
primary: `${PROVIDER_ID}/${primaryModel || models[0]?.id}`,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
writeConfig(config);
|
|
101
|
-
return { path: CONFIG_PATH, modelsCount: models.length };
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
changeKey(apiKey) {
|
|
105
|
-
const { config } = readConfig();
|
|
106
|
-
if (!config?.models?.providers?.[PROVIDER_ID]) {
|
|
107
|
-
throw new Error("NOITON não está configurado no OpenClaw");
|
|
108
|
-
}
|
|
109
|
-
config.env = config.env || {};
|
|
110
|
-
config.env.NOITON_API_KEY = apiKey;
|
|
111
|
-
writeConfig(config);
|
|
112
|
-
return { path: CONFIG_PATH };
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
refresh(models) {
|
|
116
|
-
const { config } = readConfig();
|
|
117
|
-
if (!config?.models?.providers?.[PROVIDER_ID]) {
|
|
118
|
-
throw new Error("NOITON não está configurado no OpenClaw");
|
|
119
|
-
}
|
|
120
|
-
config.models.providers[PROVIDER_ID].models = models.map((m) => ({
|
|
121
|
-
id: m.id,
|
|
122
|
-
name: prettifyName(m.id),
|
|
123
|
-
reasoning: false,
|
|
124
|
-
input: ["text"],
|
|
125
|
-
contextWindow: m.context_window || 32768,
|
|
126
|
-
maxTokens: 4096,
|
|
127
|
-
}));
|
|
128
|
-
writeConfig(config);
|
|
129
|
-
return { path: CONFIG_PATH, modelsCount: models.length };
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
remove() {
|
|
133
|
-
const { config } = readConfig();
|
|
134
|
-
let removed = false;
|
|
135
|
-
if (config?.models?.providers?.[PROVIDER_ID]) {
|
|
136
|
-
delete config.models.providers[PROVIDER_ID];
|
|
137
|
-
removed = true;
|
|
138
|
-
}
|
|
139
|
-
if (config?.env?.NOITON_API_KEY) {
|
|
140
|
-
delete config.env.NOITON_API_KEY;
|
|
141
|
-
removed = true;
|
|
142
|
-
}
|
|
143
|
-
if (removed) writeConfig(config);
|
|
144
|
-
return { path: CONFIG_PATH, removed };
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
function prettifyName(id) {
|
|
149
|
-
return id
|
|
150
|
-
.replace(/[@/]/g, " ")
|
|
151
|
-
.split(/[-_]/)
|
|
152
|
-
.filter(Boolean)
|
|
153
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
154
|
-
.join(" ");
|
|
155
|
-
}
|