empuno 1.0.1 → 1.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/README.md +18 -0
- package/empuno.mjs +279 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,6 +51,22 @@ komutu CLI güncellemesi olmadan görünür.
|
|
|
51
51
|
- `--json '{…}'` / `--json @govde.json`, `--dry-run`, `--output yol`,
|
|
52
52
|
`--profile ad` (birden çok firma).
|
|
53
53
|
|
|
54
|
+
## Talepler (empuno ekibine)
|
|
55
|
+
|
|
56
|
+
Ajanınız yapamadığı bir işlemi, bir hatayı ya da geliştirme önerisini empuno
|
|
57
|
+
ekibine talep olarak iletir; yazışma çok mesajlıdır, ekip yanıtlar ve kapatır.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
empuno tickets open "Toplu izin onayı" --kind feature --body "Bekleyen izinleri tek komutla onaylamak istiyoruz." --command leave-requests.approve
|
|
61
|
+
empuno tickets --unread # ekibin yanıtladıkları
|
|
62
|
+
empuno tickets show TK-7Q3M9X # yazışmayı oku
|
|
63
|
+
empuno tickets reply TK-7Q3M9X "Her ay sonu gerekiyor."
|
|
64
|
+
empuno tickets close TK-7Q3M9X --note "Çözüldü"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Türler: `feature`, `bug`, `criticism`, `question`, `missing_command`, `docs`.
|
|
68
|
+
Gövde için `--body @not.md` ya da `--body -` (stdin) kullanılabilir.
|
|
69
|
+
|
|
54
70
|
## Çıktı sözleşmesi
|
|
55
71
|
|
|
56
72
|
- stdout **her zaman JSON** (TTY'de girintili).
|
|
@@ -66,6 +82,8 @@ empuno İK sistemine `empuno` CLI ile erişimin var. Önce `empuno commands <ana
|
|
|
66
82
|
ile komutu bul, `empuno describe <komut>` ile parametrelerini öğren, sonra çalıştır.
|
|
67
83
|
422 → `errors` alanlarını düzeltip tekrar dene; 403 → yetki yetmiyor, kullanıcıya
|
|
68
84
|
`required_permission`ı söyle. Geri alınamaz işlemlerden önce kullanıcıdan onay al.
|
|
85
|
+
Yapamadığın işlem / hata / öneri için: empuno tickets open "<başlık>" --kind missing_command|bug|feature --body "…";
|
|
86
|
+
yanıtları `empuno tickets --unread` ile kontrol et.
|
|
69
87
|
```
|
|
70
88
|
|
|
71
89
|
## Güvenlik
|
package/empuno.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import os from 'node:os';
|
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
import crypto from 'node:crypto';
|
|
23
23
|
|
|
24
|
-
const VERSION = '1.
|
|
24
|
+
const VERSION = '1.1.1'; // EMPUNO_CLI_VERSION — config/agent_api.php cli_version + package.json ile aynı tutulur
|
|
25
25
|
const NPM_PACKAGE = 'empuno';
|
|
26
26
|
const DEFAULT_URL = 'https://empuno.com';
|
|
27
27
|
const CATALOG_TTL_MS = 10 * 60 * 1000;
|
|
@@ -89,12 +89,13 @@ function resolveAuth(flags) {
|
|
|
89
89
|
const profile = cfg.profiles?.[profileName] || {};
|
|
90
90
|
const key = flags.key || process.env.EMPUNO_API_KEY || profile.key || null;
|
|
91
91
|
const url = (flags.url || process.env.EMPUNO_URL || profile.url || DEFAULT_URL).replace(/\/+$/, '');
|
|
92
|
-
|
|
92
|
+
const staffToken = flags.token || process.env.EMPUNO_STAFF_TOKEN || profile.staff_token || null;
|
|
93
|
+
return { key, url, profileName, staffToken };
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
function requireKey(auth) {
|
|
96
97
|
if (!auth.key) {
|
|
97
|
-
throw new UsageError('API anahtarı yok. `empuno login` çalıştırın ya da EMPUNO_API_KEY ortam değişkenini ayarlayın. Anahtar: empuno paneli → Firma Ayarları → API → "Ajan" türünde anahtar.');
|
|
98
|
+
throw new UsageError('API anahtarı yok. `empuno login` çalıştırın ya da EMPUNO_API_KEY ortam değişkenini ayarlayın. Anahtar: empuno paneli → Firma Ayarları → API & Ajan → "Ajan" türünde anahtar.');
|
|
98
99
|
}
|
|
99
100
|
if (!auth.key.startsWith('emp_live_')) {
|
|
100
101
|
throw new UsageError('Geçersiz anahtar biçimi (emp_live_ ile başlamalı).');
|
|
@@ -103,14 +104,15 @@ function requireKey(auth) {
|
|
|
103
104
|
|
|
104
105
|
// ─── argüman ayrıştırma ────────────────────────────────────────────────────
|
|
105
106
|
|
|
106
|
-
const GLOBAL_FLAGS = new Set(['key', 'url', 'profile', 'pretty', 'compact', 'text', 'json-out', 'dry-run', 'output', 'refresh', 'all', 'full', 'module', 'writes', 'reads', 'help', 'key-stdin', 'query', 'json', 'file', 'quiet']);
|
|
107
|
+
const GLOBAL_FLAGS = new Set(['key', 'token', 'url', 'profile', 'pretty', 'compact', 'text', 'json-out', 'dry-run', 'output', 'refresh', 'all', 'full', 'module', 'writes', 'reads', 'help', 'key-stdin', 'query', 'json', 'file', 'quiet']);
|
|
107
108
|
const BOOLEAN_FLAGS = new Set(['pretty', 'compact', 'text', 'json-out', 'dry-run', 'refresh', 'all', 'full', 'writes', 'reads', 'help', 'key-stdin', 'quiet']);
|
|
108
109
|
|
|
109
110
|
/**
|
|
110
111
|
* argv → { positional: [], flags: {global}, fields: [[name, rawValue]] }.
|
|
111
112
|
* `--ad değer`, `--ad=değer`, tekrar eden bayrak → dizi, `--` sonrası hep positional.
|
|
112
113
|
*/
|
|
113
|
-
function parseArgs(argv) {
|
|
114
|
+
function parseArgs(argv, extraBooleans = []) {
|
|
115
|
+
const extraBool = new Set(extraBooleans);
|
|
114
116
|
const positional = [];
|
|
115
117
|
const flags = { query: [], file: [] };
|
|
116
118
|
const fields = [];
|
|
@@ -134,7 +136,7 @@ function parseArgs(argv) {
|
|
|
134
136
|
name = name.slice(0, eq);
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
if (GLOBAL_FLAGS.has(name) && BOOLEAN_FLAGS.has(name)) {
|
|
139
|
+
if ((GLOBAL_FLAGS.has(name) && BOOLEAN_FLAGS.has(name)) || extraBool.has(name)) {
|
|
138
140
|
flags[name] = value === undefined ? true : !['0', 'false', 'no'].includes(value);
|
|
139
141
|
i++;
|
|
140
142
|
continue;
|
|
@@ -380,7 +382,7 @@ async function findCommand(auth, name, flags) {
|
|
|
380
382
|
if (cmd) return cmd;
|
|
381
383
|
|
|
382
384
|
if (catalog.access === 'read') {
|
|
383
|
-
const err = new UsageError('bu anahtar salt-okuma ("read") türünde: panel komutlarını çalıştıramaz, yalnız `empuno api …`. Firma Ayarları → API sekmesinden "Ajan" anahtarı oluşturun.');
|
|
385
|
+
const err = new UsageError('bu anahtar salt-okuma ("read") türünde: panel komutlarını çalıştıramaz, yalnız `empuno api …`. Firma Ayarları → API & Ajan sekmesinden "Ajan" anahtarı oluşturun.');
|
|
384
386
|
err.payload = { ok: false, error: 'read_only_key' };
|
|
385
387
|
throw err;
|
|
386
388
|
}
|
|
@@ -392,8 +394,8 @@ async function findCommand(auth, name, flags) {
|
|
|
392
394
|
|
|
393
395
|
if (cmd) {
|
|
394
396
|
const need = cmd.requires_company_admin ? ['company_admin'] : cmd.permissions;
|
|
395
|
-
const err = new UsageError(`"${name}" bu anahtarın kapsamında değil. Gereken yetki: ${need.join(', ') || '—'}. Anahtar kapsamını panelden genişletin (Firma Ayarları → API).`);
|
|
396
|
-
err.payload = { ok: false, error: 'scope_required', command: name, required: need };
|
|
397
|
+
const err = new UsageError(`"${name}" bu anahtarın kapsamında değil. Gereken yetki: ${need.join(', ') || '—'}. Anahtar kapsamını panelden genişletin (Firma Ayarları → API & Ajan).`);
|
|
398
|
+
err.payload = { ok: false, error: 'scope_required', command: name, required: need, hint: 'Kapsamı anahtar sahibi panelden genişletir; kullanıcıya bildirin.' };
|
|
397
399
|
throw err;
|
|
398
400
|
}
|
|
399
401
|
|
|
@@ -402,7 +404,13 @@ async function findCommand(auth, name, flags) {
|
|
|
402
404
|
.filter((c) => c.includes(name.split('.')[0]) || name.includes(c.split('.')[0]))
|
|
403
405
|
.slice(0, 8);
|
|
404
406
|
const err = new UsageError(`bilinmeyen komut "${name}".${suggestions.length ? ` Şunlardan biri mi: ${suggestions.join(', ')}` : ' `empuno commands` ile listeleyin.'}`);
|
|
405
|
-
err.payload = {
|
|
407
|
+
err.payload = {
|
|
408
|
+
ok: false,
|
|
409
|
+
error: 'unknown_command',
|
|
410
|
+
command: name,
|
|
411
|
+
suggestions,
|
|
412
|
+
hint: `Gereken işlem yoksa talep açın: empuno tickets open "<başlık>" --kind missing_command --command ${name} --body "<ne yapmak istediğiniz>"`,
|
|
413
|
+
};
|
|
406
414
|
throw err;
|
|
407
415
|
}
|
|
408
416
|
|
|
@@ -671,6 +679,244 @@ async function cmdUpdate(args) {
|
|
|
671
679
|
return 0;
|
|
672
680
|
}
|
|
673
681
|
|
|
682
|
+
// ─── talepler (ticket) ─────────────────────────────────────────────────────
|
|
683
|
+
|
|
684
|
+
const TICKET_KINDS = {
|
|
685
|
+
feature: 'Geliştirme isteği',
|
|
686
|
+
bug: 'Hata bildirimi',
|
|
687
|
+
criticism: 'Eleştiri / geri bildirim',
|
|
688
|
+
question: 'Soru',
|
|
689
|
+
missing_command: 'Eksik komut / işlem',
|
|
690
|
+
docs: 'Dokümantasyon',
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
/** Alan bayraklarını nesneye çevirir (son değer kazanır). */
|
|
694
|
+
function fieldMap(fields) {
|
|
695
|
+
const out = {};
|
|
696
|
+
for (const [k, v] of fields) out[k] = v;
|
|
697
|
+
return out;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** "@dosya.md" → dosya içeriği; "-" → stdin; diğerleri olduğu gibi. */
|
|
701
|
+
function readText(value) {
|
|
702
|
+
if (value === undefined || value === null || value === true) return undefined;
|
|
703
|
+
const text = String(value);
|
|
704
|
+
if (text === '-') return fs.readFileSync(0, 'utf8');
|
|
705
|
+
if (text.startsWith('@') && text.length > 1) {
|
|
706
|
+
const file = text.slice(1);
|
|
707
|
+
if (!fs.existsSync(file)) throw new UsageError(`dosya bulunamadı: ${file}`);
|
|
708
|
+
return fs.readFileSync(file, 'utf8');
|
|
709
|
+
}
|
|
710
|
+
return text;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function ticketNumber(value) {
|
|
714
|
+
if (!value) throw new UsageError('talep numarası gerekli (ör. TK-7Q3M9X)');
|
|
715
|
+
const n = String(value).toUpperCase();
|
|
716
|
+
return n.startsWith('TK-') ? n : `TK-${n}`;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function printTicketList(result, perspective) {
|
|
720
|
+
if (!wantsText() || !Array.isArray(result?.data)) {
|
|
721
|
+
print(result);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
for (const t of result.data) {
|
|
725
|
+
const dot = t.unread ? '●' : ' ';
|
|
726
|
+
const who = perspective === 'staff' && t.company ? ` · ${t.company.name}` : '';
|
|
727
|
+
process.stdout.write(`${dot} ${t.number} ${t.status.padEnd(8)} ${t.kind.padEnd(15)} ${t.title}${who}\n`);
|
|
728
|
+
}
|
|
729
|
+
const total = result.meta?.total ?? result.data.length;
|
|
730
|
+
process.stdout.write(`\n${total} talep. Ayrıntı: empuno ${perspective === 'staff' ? 'support' : 'tickets'} show <numara>\n`);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function printTicket(result) {
|
|
734
|
+
const t = result?.data;
|
|
735
|
+
if (!wantsText() || !t?.messages) {
|
|
736
|
+
print(result);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
process.stdout.write(`${t.number} · ${t.title}\n${t.kind_label} · öncelik ${t.priority} · ${t.status}${t.company ? ` · ${t.company.name}` : ''}\n`);
|
|
740
|
+
for (const m of t.messages) {
|
|
741
|
+
if (m.author_type === 'system') {
|
|
742
|
+
process.stdout.write(`\n — ${m.body} (${m.created_at})\n`);
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
process.stdout.write(`\n[${m.author_type === 'staff' ? 'empuno' : 'ajan'}] ${m.author} — ${m.created_at}\n${m.body}\n`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async function cmdTickets(args) {
|
|
750
|
+
const { flags, fields, positional } = args;
|
|
751
|
+
const opts = fieldMap(fields);
|
|
752
|
+
const [action = 'list', ...rest] = positional;
|
|
753
|
+
const base = '/api/company/v1/tickets';
|
|
754
|
+
|
|
755
|
+
if (action === 'kinds') {
|
|
756
|
+
print({ kinds: TICKET_KINDS, priorities: ['low', 'normal', 'high'] });
|
|
757
|
+
return 0;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
const auth = resolveAuth(flags);
|
|
761
|
+
requireKey(auth);
|
|
762
|
+
|
|
763
|
+
const send = async (method, urlPath, body = null, query = {}) => {
|
|
764
|
+
const response = await request(auth, method, urlPath, { body, query, dryRun: flags['dry-run'] });
|
|
765
|
+
return response;
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
switch (action) {
|
|
769
|
+
case 'list':
|
|
770
|
+
case 'ls': {
|
|
771
|
+
const query = {};
|
|
772
|
+
if (opts.status) query.status = opts.status;
|
|
773
|
+
if (flags.mine) query.mine = 1;
|
|
774
|
+
if (flags.unread) query.unread = 1;
|
|
775
|
+
if (opts.page) query.page = opts.page;
|
|
776
|
+
if (opts.per_page) query.per_page = opts.per_page;
|
|
777
|
+
const r = await send('GET', base, null, query);
|
|
778
|
+
if (r.dryRun) return print(r.result), 0;
|
|
779
|
+
printTicketList(r.result, 'agent');
|
|
780
|
+
return r.ok ? 0 : 1;
|
|
781
|
+
}
|
|
782
|
+
case 'open':
|
|
783
|
+
case 'new': {
|
|
784
|
+
const title = rest[0] ?? opts.title;
|
|
785
|
+
const body = readText(opts.body ?? rest[1]);
|
|
786
|
+
const kind = opts.kind ?? 'feature';
|
|
787
|
+
if (!title || !body) {
|
|
788
|
+
throw new UsageError('kullanım: empuno tickets open "<başlık>" --kind feature|bug|criticism|question|missing_command|docs --body "<açıklama>"|@dosya.md');
|
|
789
|
+
}
|
|
790
|
+
let context = opts.context ? readJsonArg(String(opts.context)) : {};
|
|
791
|
+
if (opts.command) context = { ...context, command: String(opts.command) };
|
|
792
|
+
const r = await send('POST', base, { title: String(title), kind, body, priority: opts.priority, context: Object.keys(context).length ? context : undefined });
|
|
793
|
+
if (r.dryRun) return print(r.result), 0;
|
|
794
|
+
print(r.result);
|
|
795
|
+
return r.ok ? 0 : 1;
|
|
796
|
+
}
|
|
797
|
+
case 'show':
|
|
798
|
+
case 'get': {
|
|
799
|
+
const r = await send('GET', `${base}/${ticketNumber(rest[0])}`);
|
|
800
|
+
if (r.dryRun) return print(r.result), 0;
|
|
801
|
+
printTicket(r.result);
|
|
802
|
+
return r.ok ? 0 : 1;
|
|
803
|
+
}
|
|
804
|
+
case 'reply': {
|
|
805
|
+
const body = readText(opts.body ?? rest[1]);
|
|
806
|
+
if (!body) throw new UsageError('kullanım: empuno tickets reply <numara> "<mesaj>" (ya da --body @dosya.md)');
|
|
807
|
+
const r = await send('POST', `${base}/${ticketNumber(rest[0])}/messages`, { body });
|
|
808
|
+
print(r.result);
|
|
809
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
810
|
+
}
|
|
811
|
+
case 'close': {
|
|
812
|
+
const note = readText(opts.note ?? rest[1]);
|
|
813
|
+
const r = await send('POST', `${base}/${ticketNumber(rest[0])}/close`, note ? { note } : {});
|
|
814
|
+
print(r.result);
|
|
815
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
816
|
+
}
|
|
817
|
+
default:
|
|
818
|
+
throw new UsageError(`bilinmeyen tickets işlemi "${action}". Kullanım: empuno tickets [list|open|show|reply|close|kinds]`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// ─── destek (yalnız empuno ekibinin ajanları — emp_staff_ jetonu) ────────────
|
|
823
|
+
|
|
824
|
+
function requireStaff(auth) {
|
|
825
|
+
if (!auth.staffToken) {
|
|
826
|
+
throw new UsageError('destek jetonu yok. `empuno support login` ya da EMPUNO_STAFF_TOKEN. Jeton: empuno root paneli → Ajan Talepleri → Destek ajanları.');
|
|
827
|
+
}
|
|
828
|
+
if (!auth.staffToken.startsWith('emp_staff_')) {
|
|
829
|
+
throw new UsageError('geçersiz destek jetonu biçimi (emp_staff_ ile başlamalı).');
|
|
830
|
+
}
|
|
831
|
+
return { ...auth, key: auth.staffToken };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async function cmdSupport(args) {
|
|
835
|
+
const { flags, fields, positional } = args;
|
|
836
|
+
const opts = fieldMap(fields);
|
|
837
|
+
const [action = 'tickets', ...rest] = positional;
|
|
838
|
+
const base = '/api/support/v1';
|
|
839
|
+
|
|
840
|
+
if (action === 'login') {
|
|
841
|
+
let token = flags.token || process.env.EMPUNO_STAFF_TOKEN || rest[0];
|
|
842
|
+
if (flags['token-stdin'] || (!token && !process.stdin.isTTY)) token = fs.readFileSync(0, 'utf8').trim();
|
|
843
|
+
else if (!token) token = await prompt('Destek jetonu (emp_staff_…): ');
|
|
844
|
+
const auth = requireStaff({ ...resolveAuth(flags), staffToken: token?.trim() });
|
|
845
|
+
const { ok, result } = await request(auth, 'GET', `${base}/me`);
|
|
846
|
+
if (!ok) {
|
|
847
|
+
print(result);
|
|
848
|
+
return 1;
|
|
849
|
+
}
|
|
850
|
+
const cfg = readConfig();
|
|
851
|
+
cfg.profiles = cfg.profiles || {};
|
|
852
|
+
const name = auth.profileName;
|
|
853
|
+
cfg.profiles[name] = { ...(cfg.profiles[name] || { url: auth.url }), staff_token: auth.key };
|
|
854
|
+
if (!cfg.default || !cfg.profiles[cfg.default]) cfg.default = name;
|
|
855
|
+
writeConfig(cfg);
|
|
856
|
+
print({ ok: true, profile: name, token: result.token, counts: result.counts });
|
|
857
|
+
return 0;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const auth = requireStaff(resolveAuth(flags));
|
|
861
|
+
const call = (method, urlPath, body = null, query = {}) => request(auth, method, urlPath, { body, query, dryRun: flags['dry-run'] });
|
|
862
|
+
|
|
863
|
+
switch (action) {
|
|
864
|
+
case 'whoami':
|
|
865
|
+
case 'me': {
|
|
866
|
+
const r = await call('GET', `${base}/me`);
|
|
867
|
+
print(r.result);
|
|
868
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
869
|
+
}
|
|
870
|
+
case 'tickets':
|
|
871
|
+
case 'list':
|
|
872
|
+
case 'ls': {
|
|
873
|
+
const query = {};
|
|
874
|
+
for (const k of ['status', 'kind', 'company', 'q', 'page', 'per_page']) if (opts[k] !== undefined) query[k] = opts[k];
|
|
875
|
+
if (flags.unread) query.unread = 1;
|
|
876
|
+
const r = await call('GET', `${base}/tickets`, null, query);
|
|
877
|
+
if (r.dryRun) return print(r.result), 0;
|
|
878
|
+
printTicketList(r.result, 'staff');
|
|
879
|
+
return r.ok ? 0 : 1;
|
|
880
|
+
}
|
|
881
|
+
case 'show':
|
|
882
|
+
case 'get': {
|
|
883
|
+
const r = await call('GET', `${base}/tickets/${ticketNumber(rest[0])}`);
|
|
884
|
+
if (r.dryRun) return print(r.result), 0;
|
|
885
|
+
printTicket(r.result);
|
|
886
|
+
return r.ok ? 0 : 1;
|
|
887
|
+
}
|
|
888
|
+
case 'reply': {
|
|
889
|
+
const body = readText(opts.body ?? rest[1]);
|
|
890
|
+
if (!body) throw new UsageError('kullanım: empuno support reply <numara> "<yanıt>" [--close]');
|
|
891
|
+
const r = await call('POST', `${base}/tickets/${ticketNumber(rest[0])}/messages`, { body, close: flags.close ? true : undefined });
|
|
892
|
+
print(r.result);
|
|
893
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
894
|
+
}
|
|
895
|
+
case 'close': {
|
|
896
|
+
const note = readText(opts.note ?? rest[1]);
|
|
897
|
+
const r = await call('POST', `${base}/tickets/${ticketNumber(rest[0])}/close`, note ? { note } : {});
|
|
898
|
+
print(r.result);
|
|
899
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
900
|
+
}
|
|
901
|
+
case 'reopen': {
|
|
902
|
+
const r = await call('POST', `${base}/tickets/${ticketNumber(rest[0])}/reopen`);
|
|
903
|
+
print(r.result);
|
|
904
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
905
|
+
}
|
|
906
|
+
case 'set': {
|
|
907
|
+
const body = {};
|
|
908
|
+
if (opts.kind) body.kind = opts.kind;
|
|
909
|
+
if (opts.priority) body.priority = opts.priority;
|
|
910
|
+
if (!Object.keys(body).length) throw new UsageError('kullanım: empuno support set <numara> [--kind bug] [--priority high]');
|
|
911
|
+
const r = await call('PATCH', `${base}/tickets/${ticketNumber(rest[0])}`, body);
|
|
912
|
+
print(r.result);
|
|
913
|
+
return r.ok || r.dryRun ? 0 : 1;
|
|
914
|
+
}
|
|
915
|
+
default:
|
|
916
|
+
throw new UsageError(`bilinmeyen support işlemi "${action}". Kullanım: empuno support [login|whoami|tickets|show|reply|close|reopen|set]`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
674
920
|
const HELP = `empuno ${VERSION} — empuno İK sistemini komut satırından (ve ajanlardan) yönetin
|
|
675
921
|
|
|
676
922
|
KURULUM
|
|
@@ -700,6 +946,18 @@ ALANLAR
|
|
|
700
946
|
--alan @./dosya.pdf | --file alan=./dosya.pdf # multipart yükleme
|
|
701
947
|
--query anahtar=değer # yazma isteğinde ek sorgu parametresi
|
|
702
948
|
|
|
949
|
+
TALEPLER (geliştirme isteği, hata, eleştiri, soru → empuno ekibi)
|
|
950
|
+
empuno tickets [--status active|open|answered|closed|all] [--unread] [--mine]
|
|
951
|
+
empuno tickets open "Toplu izin onayı" --kind feature --body "…"|@not.md [--priority high] [--command leave-requests.approve]
|
|
952
|
+
empuno tickets show TK-7Q3M9X # yazışmayı okur (okunmadı bayrağı düşer)
|
|
953
|
+
empuno tickets reply TK-7Q3M9X "Ek bilgi…"
|
|
954
|
+
empuno tickets close TK-7Q3M9X [--note "Çözüldü"]
|
|
955
|
+
empuno tickets kinds # feature, bug, criticism, question, missing_command, docs
|
|
956
|
+
|
|
957
|
+
EMPUNO EKİBİ (destek jetonu emp_staff_…, EMPUNO_STAFF_TOKEN)
|
|
958
|
+
empuno support login | whoami | tickets [--unread] [--status] [--kind] [--company]
|
|
959
|
+
empuno support show|reply|close|reopen <numara> ["mesaj"] [--close] · empuno support set <numara> --priority high
|
|
960
|
+
|
|
703
961
|
GENEL BAYRAKLAR
|
|
704
962
|
--profile ad --key emp_live_… --url https://empuno.com --dry-run --output yol
|
|
705
963
|
--text | --json-out --pretty | --compact --refresh (katalog önbelleğini tazele)
|
|
@@ -714,7 +972,8 @@ BELGE: https://empuno.com/api-docs/agent
|
|
|
714
972
|
|
|
715
973
|
async function main() {
|
|
716
974
|
const argv = process.argv.slice(2);
|
|
717
|
-
const
|
|
975
|
+
const ticketish = ['tickets', 'ticket', 'support'].includes(argv.find((a) => !a.startsWith('--')) ?? '');
|
|
976
|
+
const args = parseArgs(argv, ticketish ? ['mine', 'unread', 'close', 'token-stdin'] : []);
|
|
718
977
|
const [sub, ...rest] = args.positional;
|
|
719
978
|
args.positional = rest;
|
|
720
979
|
|
|
@@ -723,6 +982,10 @@ async function main() {
|
|
|
723
982
|
return 0;
|
|
724
983
|
}
|
|
725
984
|
if (args.flags.help) {
|
|
985
|
+
if (['tickets', 'ticket', 'support', 'login', 'commands', 'describe', 'call', 'api'].includes(sub)) {
|
|
986
|
+
process.stdout.write(HELP);
|
|
987
|
+
return 0;
|
|
988
|
+
}
|
|
726
989
|
args.positional = [sub];
|
|
727
990
|
return cmdDescribe(args);
|
|
728
991
|
}
|
|
@@ -750,6 +1013,11 @@ async function main() {
|
|
|
750
1013
|
return cmdApi(args);
|
|
751
1014
|
case 'update':
|
|
752
1015
|
return cmdUpdate(args);
|
|
1016
|
+
case 'tickets':
|
|
1017
|
+
case 'ticket':
|
|
1018
|
+
return cmdTickets(args);
|
|
1019
|
+
case 'support':
|
|
1020
|
+
return cmdSupport(args);
|
|
753
1021
|
case 'run':
|
|
754
1022
|
if (!args.positional[0]) throw new UsageError('kullanım: empuno run <komut> …');
|
|
755
1023
|
return cmdRun({ ...args, positional: args.positional.slice(1) }, args.positional[0]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "empuno",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "empuno İK platformunun komut satırı: yönetim panelindeki tüm işlemleri (izin, bordro, vardiya, PDKS, zimmet, masraf, işe alım) yapay zekâ ajanlarına ve otomasyonlara açar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|