kiosapi 0.1.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/dist/help.js ADDED
@@ -0,0 +1,45 @@
1
+ import { bold, dim } from './ui.js';
2
+ export const VERSION = '0.1.0';
3
+ export function printVersion() {
4
+ console.log(`kiosapi ${VERSION}`);
5
+ }
6
+ export function printHelp() {
7
+ console.log(`${bold('kiosapi')} — CLI Kiosapi.id (Fase 0)
8
+
9
+ ${bold('Penggunaan:')} kiosapi [perintah] [opsi]
10
+ Tanpa perintah → sesi interaktif (ketik tugas; perintah meta diawali "/").
11
+
12
+ ${bold('Perintah:')}
13
+ masuk Simpan API key (kios_live_…)
14
+ keluar Hapus API key tersimpan
15
+ periksa Cek konektivitas, key, & setelan
16
+ model [--cari q] Daftar model yang tersedia
17
+ tanya "…" Tanya sekali (streaming); dukung pipe stdin
18
+ ngobrol Mode percakapan interaktif (REPL)
19
+ setel <k> <v> Atur setelan (model | base-url)
20
+ sambung <tool> Pakai Kiosapi di agen lain (aider, opencode, cursor…)
21
+ saldo Lihat saldo & pengeluaran bulan ini
22
+ pakai [--hari N] Ringkasan pemakaian N hari terakhir
23
+ isi <jumlah> Buat tagihan top-up (min 10.000)
24
+
25
+ ${bold('Agen (butuh model yang mendukung tool calling):')}
26
+ rencana "…" Telusuri kode & susun rencana (read-only)
27
+ edit "…" Lakukan perubahan kode (tulis/edit file)
28
+ buat "…" Agen otonom: bangun proyek (tulis + jalankan)
29
+ tim "…" Multi-agen: perencana → pengkode → peninjau
30
+ ${dim('Opsi: -m <model>, --otomatis (lewati konfirmasi).')}
31
+
32
+ ${bold('Multimodal:')}
33
+ gambar "…" Buat gambar → simpan file (-o file, --rasio, --opsi)
34
+ video "…" Buat video (async) → simpan file (--detik N)
35
+ lihat <file> "…" Tanya model vision tentang sebuah gambar
36
+
37
+ ${bold('Contoh:')}
38
+ kiosapi masuk
39
+ kiosapi model --cari coding
40
+ kiosapi tanya -m deepseek/deepseek-v3 "bikin regex email"
41
+ cat error.log | kiosapi tanya "jelaskan error ini"
42
+
43
+ ${dim('Alias Inggris: login, logout, doctor, models, ask, chat, config.')}
44
+ ${dim('Env: KIOSAPI_API_KEY, KIOSAPI_BASE_URL, NO_COLOR.')}`);
45
+ }
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { cmdBuat, cmdEdit, cmdGambar, cmdIsi, cmdKeluar, cmdLihat, cmdMasuk, cmdModel, cmdNgobrol, cmdPakai, cmdPeriksa, cmdRencana, cmdSaldo, cmdSambung, cmdSetel, cmdTanya, cmdTim, cmdVideo, } from './commands.js';
3
+ import { printHelp, printVersion } from './help.js';
4
+ import { startSession } from './session.js';
5
+ import { red } from './ui.js';
6
+ /** Indonesian command names with English aliases. */
7
+ const COMMANDS = {
8
+ masuk: cmdMasuk,
9
+ login: cmdMasuk,
10
+ keluar: cmdKeluar,
11
+ logout: cmdKeluar,
12
+ periksa: cmdPeriksa,
13
+ doctor: cmdPeriksa,
14
+ model: cmdModel,
15
+ models: cmdModel,
16
+ tanya: cmdTanya,
17
+ ask: cmdTanya,
18
+ ngobrol: cmdNgobrol,
19
+ chat: cmdNgobrol,
20
+ setel: cmdSetel,
21
+ config: cmdSetel,
22
+ sambung: cmdSambung,
23
+ connect: cmdSambung,
24
+ saldo: cmdSaldo,
25
+ balance: cmdSaldo,
26
+ pakai: cmdPakai,
27
+ usage: cmdPakai,
28
+ isi: cmdIsi,
29
+ topup: cmdIsi,
30
+ rencana: cmdRencana,
31
+ plan: cmdRencana,
32
+ edit: cmdEdit,
33
+ buat: cmdBuat,
34
+ agen: cmdBuat,
35
+ tim: cmdTim,
36
+ team: cmdTim,
37
+ gambar: cmdGambar,
38
+ image: cmdGambar,
39
+ video: cmdVideo,
40
+ lihat: cmdLihat,
41
+ vision: cmdLihat,
42
+ };
43
+ async function main() {
44
+ const [cmd, ...rest] = process.argv.slice(2);
45
+ if (cmd === '--help' || cmd === '-h' || cmd === 'bantuan') {
46
+ printHelp();
47
+ return;
48
+ }
49
+ if (cmd === '--version' || cmd === '-v' || cmd === 'versi') {
50
+ printVersion();
51
+ return;
52
+ }
53
+ // No subcommand: drop into the interactive session (Claude-Code-style) when attached to a
54
+ // terminal; otherwise print help (so scripts/CI don't hang on stdin).
55
+ if (!cmd) {
56
+ if (process.stdin.isTTY) {
57
+ await startSession();
58
+ }
59
+ else {
60
+ printHelp();
61
+ }
62
+ return;
63
+ }
64
+ const handler = COMMANDS[cmd];
65
+ if (!handler) {
66
+ console.error(red(`Perintah tidak dikenal: ${cmd}\n`));
67
+ printHelp();
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ await handler(rest);
72
+ }
73
+ main().catch((err) => {
74
+ console.error(red(err instanceof Error ? err.message : String(err)));
75
+ process.exitCode = 1;
76
+ });
@@ -0,0 +1,166 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { newSession, resetSession, runTurn } from './agent/run.js';
3
+ import { runTeam } from './agent/team.js';
4
+ import { resolveModel } from './api.js';
5
+ import { cmdGambar, cmdIsi, cmdLihat, cmdMasuk, cmdPakai, cmdSaldo, cmdVideo } from './commands.js';
6
+ import { loadConfig } from './config.js';
7
+ import { bold, cyan, dim, green, red } from './ui.js';
8
+ const MODES = ['rencana', 'edit', 'buat'];
9
+ /** The prompt indicator shows the active mode (and ⚡ when auto-approve is on). */
10
+ function label(s) {
11
+ return `${cyan(`${s.mode}${s.otomatis ? ' ⚡' : ''}`)} › `;
12
+ }
13
+ function banner(s) {
14
+ console.log(`${bold('Kiosapi CLI')} — sesi interaktif
15
+ Model : ${s.model}
16
+ Mode : ${s.mode} ${dim('(rencana=read-only · edit=ubah file · buat=otonom)')}
17
+
18
+ ${dim('Ketik tugasmu langsung. Perintah meta diawali "/". /bantuan untuk daftar, /keluar untuk berhenti.')}`);
19
+ }
20
+ function slashHelp() {
21
+ console.log(`${bold('Perintah sesi:')}
22
+ /tim <tugas> Multi-agen (perencana→pengkode→peninjau)
23
+ /gambar <prompt> Buat gambar → file
24
+ /video <prompt> Buat video → file
25
+ /lihat <file> <pertanyaan> Tanya model vision tentang gambar
26
+ /mode [rencana|edit|buat] Lihat/ubah mode agen
27
+ /model [id] Lihat/ubah model
28
+ /otomatis Hidup/matikan auto-approve (lewati konfirmasi)
29
+ /bersih Bersihkan riwayat percakapan
30
+ /saldo Lihat saldo
31
+ /pakai [--hari N] Ringkasan pemakaian
32
+ /isi <jumlah> Buat tagihan top-up
33
+ /bantuan Tampilkan bantuan ini
34
+ /keluar Keluar dari sesi`);
35
+ }
36
+ /** Handle a /slash command. Returns true when the session should end. */
37
+ async function runSlash(line, s) {
38
+ const [name, ...rest] = line.slice(1).split(/\s+/);
39
+ const cmd = (name ?? '').toLowerCase();
40
+ try {
41
+ switch (cmd) {
42
+ case 'keluar':
43
+ case 'exit':
44
+ case 'q':
45
+ return true;
46
+ case 'bantuan':
47
+ case 'help':
48
+ case '?':
49
+ slashHelp();
50
+ return false;
51
+ case 'mode': {
52
+ const m = rest[0]?.toLowerCase();
53
+ if (!m) {
54
+ console.log(dim(`Mode: ${s.mode}`));
55
+ }
56
+ else if (MODES.includes(m)) {
57
+ s.mode = m;
58
+ s.messages.push({ role: 'system', content: `Mode diubah ke: ${m}.` });
59
+ console.log(dim(`Mode: ${s.mode}`));
60
+ }
61
+ else {
62
+ console.log(red(`Mode tidak dikenal. Pilih: ${MODES.join(' | ')}`));
63
+ }
64
+ return false;
65
+ }
66
+ case 'model': {
67
+ const id = rest[0];
68
+ if (id) {
69
+ s.model = id;
70
+ console.log(dim(`Model: ${s.model}`));
71
+ }
72
+ else {
73
+ console.log(dim(`Model: ${s.model}`));
74
+ }
75
+ return false;
76
+ }
77
+ case 'otomatis':
78
+ s.otomatis = !s.otomatis;
79
+ console.log(dim(`Auto-approve: ${s.otomatis ? 'AKTIF' : 'nonaktif'}`));
80
+ return false;
81
+ case 'bersih':
82
+ resetSession(s);
83
+ console.log(dim('Riwayat dibersihkan.'));
84
+ return false;
85
+ case 'saldo':
86
+ await cmdSaldo();
87
+ return false;
88
+ case 'pakai':
89
+ await cmdPakai(rest);
90
+ return false;
91
+ case 'isi':
92
+ await cmdIsi(rest);
93
+ return false;
94
+ case 'tim':
95
+ case 'team': {
96
+ const task = rest.join(' ').trim();
97
+ if (!task)
98
+ console.log(red('Beri tugas. Contoh: /tim bikin endpoint /health + tes'));
99
+ else
100
+ await runTeam(task, { model: s.model, otomatis: s.otomatis });
101
+ return false;
102
+ }
103
+ case 'gambar':
104
+ await cmdGambar(rest);
105
+ return false;
106
+ case 'video':
107
+ await cmdVideo(rest);
108
+ return false;
109
+ case 'lihat':
110
+ await cmdLihat(rest);
111
+ return false;
112
+ default:
113
+ console.log(red(`Perintah tidak dikenal: /${cmd} (coba /bantuan)`));
114
+ return false;
115
+ }
116
+ }
117
+ catch (err) {
118
+ console.error(red(err instanceof Error ? err.message : String(err)));
119
+ return false;
120
+ }
121
+ }
122
+ /**
123
+ * Interactive session (Claude-Code-style): run `kiosapi` with no args to enter. Plain text is a
124
+ * prompt to the agent (with persistent context); `/command` runs a meta command. Logs in first if
125
+ * needed.
126
+ */
127
+ export async function startSession() {
128
+ if (!loadConfig().apiKey) {
129
+ console.log(dim('Belum masuk — masukkan API key dulu.'));
130
+ await cmdMasuk();
131
+ if (!loadConfig().apiKey)
132
+ return;
133
+ }
134
+ const model = await resolveModel(undefined);
135
+ const s = newSession(model, 'buat', false);
136
+ banner(s);
137
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
138
+ try {
139
+ while (true) {
140
+ let line;
141
+ try {
142
+ line = (await rl.question(label(s))).trim();
143
+ }
144
+ catch {
145
+ break; // stdin closed (Ctrl+D / EOF)
146
+ }
147
+ if (!line)
148
+ continue;
149
+ if (line.startsWith('/')) {
150
+ if (await runSlash(line, s))
151
+ break;
152
+ continue;
153
+ }
154
+ try {
155
+ await runTurn(s, line);
156
+ }
157
+ catch (err) {
158
+ console.error(red(err instanceof Error ? err.message : String(err)));
159
+ }
160
+ }
161
+ }
162
+ finally {
163
+ rl.close();
164
+ }
165
+ console.log(green('Sampai jumpa.'));
166
+ }
package/dist/ui.js ADDED
@@ -0,0 +1,99 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { createInterface as createPromiseInterface } from 'node:readline/promises';
3
+ const useColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
4
+ const paint = (code, s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : s);
5
+ export const bold = (s) => paint('1', s);
6
+ export const dim = (s) => paint('2', s);
7
+ export const red = (s) => paint('31', s);
8
+ export const green = (s) => paint('32', s);
9
+ export const yellow = (s) => paint('33', s);
10
+ export const cyan = (s) => paint('36', s);
11
+ /** Format a number as Indonesian Rupiah, e.g. 50000 → "Rp50.000". */
12
+ export const rupiah = (n) => `Rp${Math.round(n).toLocaleString('id-ID')}`;
13
+ /** Group a number with Indonesian thousands separators. */
14
+ export const idn = (n) => n.toLocaleString('id-ID');
15
+ /** Resolve after `ms` milliseconds (used for polling). */
16
+ export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
17
+ /** Rotating tips shown while the model is thinking. */
18
+ export const THINKING_TIPS = [
19
+ 'Ketik /bantuan untuk daftar perintah.',
20
+ '/mode rencana|edit|buat untuk ganti mode agen.',
21
+ '/otomatis untuk lewati konfirmasi tulis/jalankan.',
22
+ 'kiosapi tim "…" menjalankan multi-agen (perencana→pengkode→peninjau).',
23
+ 'kiosapi gambar "…" membuat gambar; lihat <file> "…" untuk vision.',
24
+ 'Untuk agen, pakai model tool-capable (mis. anthropic/claude-sonnet-4-6).',
25
+ 'kiosapi sambung aider — pakai Kiosapi di tool agen lain.',
26
+ '.kiosapiignore melindungi file (mis. rahasia) dari agen.',
27
+ '/saldo & /pakai untuk cek saldo dan pemakaian.',
28
+ ];
29
+ const SPINNER = ['|', '/', '-', '\\'];
30
+ /**
31
+ * Show an animated "berpikir…" line with a rotating tip while waiting for the model. No-op when not
32
+ * attached to a TTY (so pipes/CI stay clean). Returns a stop function that clears the line.
33
+ */
34
+ export function thinking(tips = THINKING_TIPS) {
35
+ if (!process.stdout.isTTY)
36
+ return () => { };
37
+ let i = 0;
38
+ let tip = Math.floor(Math.random() * tips.length);
39
+ const render = () => {
40
+ const frame = SPINNER[i % SPINNER.length] ?? '|';
41
+ process.stdout.write(`\x1b[2K\x1b[0G${cyan(frame)} ${dim(`berpikir… ${tips[tip] ?? ''}`)}`);
42
+ i++;
43
+ if (i % 12 === 0)
44
+ tip = (tip + 1) % tips.length; // rotate tip roughly every second
45
+ };
46
+ render();
47
+ const timer = setInterval(render, 90);
48
+ return () => {
49
+ clearInterval(timer);
50
+ process.stdout.write('\x1b[2K\x1b[0G'); // clear the spinner line
51
+ };
52
+ }
53
+ /** Ask a question and return the typed line. */
54
+ export async function prompt(question) {
55
+ const rl = createPromiseInterface({ input: process.stdin, output: process.stdout });
56
+ try {
57
+ return await rl.question(question);
58
+ }
59
+ finally {
60
+ rl.close();
61
+ }
62
+ }
63
+ /**
64
+ * Ask for a secret (API key), echoing a "*" per character instead of the real text — so a paste is
65
+ * visibly acknowledged (a fully blank field made users think paste failed). On each change we clear
66
+ * the line and reprint the prompt + one star per typed char (handles paste & backspace correctly).
67
+ */
68
+ export function promptHidden(question) {
69
+ return new Promise((resolve) => {
70
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
71
+ const internal = rl;
72
+ let muted = false;
73
+ internal._writeToOutput = (str) => {
74
+ if (muted) {
75
+ // \x1b[2K = clear line, \x1b[0G = cursor to column 1.
76
+ internal.output.write(`\x1b[2K\x1b[0G${question}${'*'.repeat(internal.line.length)}`);
77
+ }
78
+ else {
79
+ internal.output.write(str);
80
+ }
81
+ };
82
+ process.stdout.write(question);
83
+ muted = true;
84
+ rl.question('', (answer) => {
85
+ rl.close();
86
+ process.stdout.write('\n');
87
+ resolve(answer);
88
+ });
89
+ });
90
+ }
91
+ /** Read all of stdin when the CLI is being piped into (e.g. `cat x | kiosapi tanya …`). */
92
+ export async function readStdin() {
93
+ if (process.stdin.isTTY)
94
+ return '';
95
+ const chunks = [];
96
+ for await (const chunk of process.stdin)
97
+ chunks.push(chunk);
98
+ return Buffer.concat(chunks).toString('utf8').trim();
99
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "kiosapi",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
+ "keywords": [
7
+ "kiosapi",
8
+ "ai",
9
+ "cli",
10
+ "openai",
11
+ "llm",
12
+ "agent",
13
+ "coding-agent",
14
+ "gateway",
15
+ "indonesia"
16
+ ],
17
+ "homepage": "https://kiosapi.id",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/usethea/kiosapi.git",
21
+ "directory": "apps/cli"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/usethea/kiosapi/issues"
25
+ },
26
+ "license": "MIT",
27
+ "author": "PT Mura Teras Kreatif",
28
+ "bin": {
29
+ "kiosapi": "./dist/index.js"
30
+ },
31
+ "files": ["dist", "README.md"],
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.json",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit",
38
+ "prepublishOnly": "tsc -p tsconfig.json"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.0",
42
+ "typescript": "^5.7.3"
43
+ }
44
+ }