kiosapi 0.1.0 → 0.1.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/api.js +9 -0
- package/dist/commands.js +63 -19
- package/dist/help.js +2 -2
- package/dist/session.js +11 -7
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -21,6 +21,15 @@ export async function fetchModels() {
|
|
|
21
21
|
const body = (await res.json());
|
|
22
22
|
return body.data ?? [];
|
|
23
23
|
}
|
|
24
|
+
/** Best-effort: does a model support tool calling? Defaults to true if unknown/offline. */
|
|
25
|
+
export async function modelSupportsTools(id) {
|
|
26
|
+
try {
|
|
27
|
+
return (await fetchModels()).find((m) => m.id === id)?.tools !== false;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
24
33
|
/** Pick the model to use: explicit flag → configured default → first free text model. */
|
|
25
34
|
export async function resolveModel(flag) {
|
|
26
35
|
if (flag)
|
package/dist/commands.js
CHANGED
|
@@ -5,9 +5,9 @@ import { createInterface } from 'node:readline/promises';
|
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
6
|
import { runAgent } from './agent/run.js';
|
|
7
7
|
import { runTeam } from './agent/team.js';
|
|
8
|
-
import { createTopup, fetchBytesAuthed, fetchModels, fetchPemakaian, fetchSaldo, generateImage, pollJob, resolveMediaModel, resolveModel, streamChat, streamVision, submitVideo, } from './api.js';
|
|
8
|
+
import { createTopup, fetchBytesAuthed, fetchModels, fetchPemakaian, fetchSaldo, generateImage, modelSupportsTools, pollJob, resolveMediaModel, resolveModel, streamChat, streamVision, submitVideo, } from './api.js';
|
|
9
9
|
import { clearKey, fileConfig, loadConfig, saveConfig } from './config.js';
|
|
10
|
-
import { bold, cyan, dim, green, idn, promptHidden, readStdin, red, rupiah, sleep, thinking, yellow, } from './ui.js';
|
|
10
|
+
import { bold, cyan, dim, green, idn, prompt, promptHidden, readStdin, red, rupiah, sleep, thinking, yellow, } from './ui.js';
|
|
11
11
|
const IMAGE_MIME = {
|
|
12
12
|
'.png': 'image/png',
|
|
13
13
|
'.jpg': 'image/jpeg',
|
|
@@ -49,18 +49,18 @@ export async function cmdPeriksa() {
|
|
|
49
49
|
console.log(red('tidak terhubung'));
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
/** model — list available models
|
|
52
|
+
/** model — list available models. `--cari q` filters; `--tools` shows only tool-capable models. */
|
|
53
53
|
export async function cmdModel(args) {
|
|
54
54
|
const { values } = parseArgs({
|
|
55
55
|
args,
|
|
56
|
-
options: { cari: { type: 'string', short: 'c' } },
|
|
56
|
+
options: { cari: { type: 'string', short: 'c' }, tools: { type: 'boolean' } },
|
|
57
57
|
allowPositionals: true,
|
|
58
58
|
});
|
|
59
59
|
const q = (values.cari ?? '').toLowerCase();
|
|
60
60
|
const models = await fetchModels();
|
|
61
|
-
const rows =
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const rows = models
|
|
62
|
+
.filter((m) => !values.tools || m.tools)
|
|
63
|
+
.filter((m) => !q || m.id.toLowerCase().includes(q) || (m.provider ?? '').toLowerCase().includes(q));
|
|
64
64
|
if (rows.length === 0) {
|
|
65
65
|
console.log(dim('Tidak ada model yang cocok.'));
|
|
66
66
|
return;
|
|
@@ -69,9 +69,16 @@ export async function cmdModel(args) {
|
|
|
69
69
|
for (const m of rows) {
|
|
70
70
|
const tier = (m.tier ?? '').padEnd(5);
|
|
71
71
|
const kind = (m.kind ?? 'text').padEnd(5);
|
|
72
|
-
|
|
72
|
+
const wrench = m.tools ? '🔧' : ' ';
|
|
73
|
+
console.log(`${wrench} ${m.id.padEnd(width)} ${dim(tier)} ${kind} ${dim(m.provider ?? '')}`);
|
|
74
|
+
}
|
|
75
|
+
console.log(dim(`\n${rows.length} model. 🔧 = mendukung tool calling (untuk agen).`));
|
|
76
|
+
}
|
|
77
|
+
/** Warn (best-effort) if the chosen model likely can't drive the agent's tools. */
|
|
78
|
+
export async function warnIfNoTools(model) {
|
|
79
|
+
if (!(await modelSupportsTools(model))) {
|
|
80
|
+
console.error(yellow(`⚠ Model "${model}" mungkin tak mendukung tool calling — agen bisa tak bekerja. Pilih model 🔧 (lihat: kiosapi model --tools), mis. anthropic/claude-sonnet-4-6.`));
|
|
73
81
|
}
|
|
74
|
-
console.log(dim(`\n${rows.length} model.`));
|
|
75
82
|
}
|
|
76
83
|
/** tanya — one-shot streaming question (also reads piped stdin as context). */
|
|
77
84
|
export async function cmdTanya(args) {
|
|
@@ -164,23 +171,58 @@ export async function cmdNgobrol(args) {
|
|
|
164
171
|
rl.close();
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
|
-
/**
|
|
168
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Interactive model picker: list text models (optionally filtered), choose by number. Returns the
|
|
176
|
+
* chosen id, or null if cancelled. Shared by `/model` (session) and `kiosapi setel model`.
|
|
177
|
+
*/
|
|
178
|
+
export async function pickModel(filter, current) {
|
|
179
|
+
const q = (filter ?? '').toLowerCase();
|
|
180
|
+
const models = (await fetchModels())
|
|
181
|
+
.filter((m) => (m.kind ?? 'text') === 'text')
|
|
182
|
+
.filter((m) => !q || m.id.toLowerCase().includes(q) || (m.provider ?? '').toLowerCase().includes(q));
|
|
183
|
+
if (models.length === 0) {
|
|
184
|
+
console.log(dim(q ? `Tidak ada model cocok "${filter}".` : 'Tidak ada model.'));
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
models.forEach((m, i) => {
|
|
188
|
+
const mark = m.id === current ? green('●') : ' ';
|
|
189
|
+
const wrench = m.tools ? '🔧' : ' ';
|
|
190
|
+
console.log(` ${String(i + 1).padStart(2)}. ${mark} ${wrench} ${m.id} ${dim(m.tier ?? '')}`);
|
|
191
|
+
});
|
|
192
|
+
const ans = (await prompt('Pilih nomor (Enter = batal): ')).trim();
|
|
193
|
+
if (!ans)
|
|
194
|
+
return null;
|
|
195
|
+
const pick = models[Number(ans) - 1];
|
|
196
|
+
if (!pick) {
|
|
197
|
+
console.log(red('Nomor tidak valid.'));
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return pick.id;
|
|
201
|
+
}
|
|
202
|
+
/** setel — read/write persisted settings. `setel model` with no value opens the picker. */
|
|
203
|
+
export async function cmdSetel(args) {
|
|
169
204
|
const [key, ...rest] = args;
|
|
170
205
|
if (!key) {
|
|
171
206
|
console.log(JSON.stringify(fileConfig(), null, 2));
|
|
172
207
|
return;
|
|
173
208
|
}
|
|
174
209
|
const value = rest.join(' ').trim();
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
if (key === 'model' || key === 'model-default') {
|
|
211
|
+
const chosen = value || (await pickModel(undefined, fileConfig().defaultModel));
|
|
212
|
+
if (!chosen)
|
|
213
|
+
return;
|
|
214
|
+
saveConfig({ defaultModel: chosen });
|
|
215
|
+
console.log(green(`✓ model = ${chosen}`));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (key === 'base-url' || key === 'url') {
|
|
219
|
+
if (!value)
|
|
220
|
+
throw new Error('Beri nilai. Contoh: kiosapi setel base-url https://api.kiosapi.id');
|
|
180
221
|
saveConfig({ baseUrl: value });
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
222
|
+
console.log(green(`✓ base-url = ${value}`));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
throw new Error(`Kunci tidak dikenal: ${key} (gunakan: model | base-url)`);
|
|
184
226
|
}
|
|
185
227
|
/** Shared driver for the agent modes (rencana/edit/buat). */
|
|
186
228
|
async function runAgentCommand(args, mode) {
|
|
@@ -193,6 +235,7 @@ async function runAgentCommand(args, mode) {
|
|
|
193
235
|
if (!task)
|
|
194
236
|
throw new Error('Beri tugas. Contoh: kiosapi buat "bikin REST API Express + tes"');
|
|
195
237
|
const model = await resolveModel(values.model);
|
|
238
|
+
await warnIfNoTools(model);
|
|
196
239
|
await runAgent(task, mode, { model, otomatis: Boolean(values.otomatis) });
|
|
197
240
|
}
|
|
198
241
|
/** rencana — read-only: explore the codebase and produce a plan. */
|
|
@@ -313,6 +356,7 @@ export async function cmdTim(args) {
|
|
|
313
356
|
if (!task)
|
|
314
357
|
throw new Error('Beri tugas. Contoh: kiosapi tim "bikin endpoint /health + tes"');
|
|
315
358
|
const model = await resolveModel(values.model);
|
|
359
|
+
await warnIfNoTools(model);
|
|
316
360
|
await runTeam(task, { model, otomatis: Boolean(values.otomatis) });
|
|
317
361
|
}
|
|
318
362
|
/** saldo — show own balance, bonus tokens, and month-to-date spend. */
|
package/dist/help.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { bold, dim } from './ui.js';
|
|
2
|
-
export const VERSION = '0.1.
|
|
2
|
+
export const VERSION = '0.1.1';
|
|
3
3
|
export function printVersion() {
|
|
4
4
|
console.log(`kiosapi ${VERSION}`);
|
|
5
5
|
}
|
|
@@ -13,7 +13,7 @@ ${bold('Perintah:')}
|
|
|
13
13
|
masuk Simpan API key (kios_live_…)
|
|
14
14
|
keluar Hapus API key tersimpan
|
|
15
15
|
periksa Cek konektivitas, key, & setelan
|
|
16
|
-
model [--cari q] Daftar model yang
|
|
16
|
+
model [--cari q] Daftar model (--tools = hanya yang mendukung tool/agen; 🔧)
|
|
17
17
|
tanya "…" Tanya sekali (streaming); dukung pipe stdin
|
|
18
18
|
ngobrol Mode percakapan interaktif (REPL)
|
|
19
19
|
setel <k> <v> Atur setelan (model | base-url)
|
package/dist/session.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createInterface } from 'node:readline/promises';
|
|
|
2
2
|
import { newSession, resetSession, runTurn } from './agent/run.js';
|
|
3
3
|
import { runTeam } from './agent/team.js';
|
|
4
4
|
import { resolveModel } from './api.js';
|
|
5
|
-
import { cmdGambar, cmdIsi, cmdLihat, cmdMasuk, cmdPakai, cmdSaldo, cmdVideo } from './commands.js';
|
|
5
|
+
import { cmdGambar, cmdIsi, cmdLihat, cmdMasuk, cmdPakai, cmdSaldo, cmdVideo, pickModel, warnIfNoTools, } from './commands.js';
|
|
6
6
|
import { loadConfig } from './config.js';
|
|
7
7
|
import { bold, cyan, dim, green, red } from './ui.js';
|
|
8
8
|
const MODES = ['rencana', 'edit', 'buat'];
|
|
@@ -24,7 +24,7 @@ function slashHelp() {
|
|
|
24
24
|
/video <prompt> Buat video → file
|
|
25
25
|
/lihat <file> <pertanyaan> Tanya model vision tentang gambar
|
|
26
26
|
/mode [rencana|edit|buat] Lihat/ubah mode agen
|
|
27
|
-
/model [id]
|
|
27
|
+
/model [id|filter] Pilih model (tanpa argumen = daftar bernomor)
|
|
28
28
|
/otomatis Hidup/matikan auto-approve (lewati konfirmasi)
|
|
29
29
|
/bersih Bersihkan riwayat percakapan
|
|
30
30
|
/saldo Lihat saldo
|
|
@@ -64,14 +64,17 @@ async function runSlash(line, s) {
|
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
case 'model': {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
const arg = rest.join(' ').trim();
|
|
68
|
+
// A full id (has "/") is set directly; otherwise open the numbered picker (filtered by arg).
|
|
69
|
+
if (arg.includes('/')) {
|
|
70
|
+
s.model = arg;
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
|
|
73
|
+
const picked = await pickModel(arg || undefined, s.model);
|
|
74
|
+
if (picked)
|
|
75
|
+
s.model = picked;
|
|
74
76
|
}
|
|
77
|
+
console.log(dim(`Model: ${s.model}`));
|
|
75
78
|
return false;
|
|
76
79
|
}
|
|
77
80
|
case 'otomatis':
|
|
@@ -134,6 +137,7 @@ export async function startSession() {
|
|
|
134
137
|
const model = await resolveModel(undefined);
|
|
135
138
|
const s = newSession(model, 'buat', false);
|
|
136
139
|
banner(s);
|
|
140
|
+
await warnIfNoTools(model);
|
|
137
141
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
138
142
|
try {
|
|
139
143
|
while (true) {
|