empuno 1.1.1 → 1.3.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/README.md +4 -0
- package/empuno.mjs +180 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ empuno leave-requests.approve 123
|
|
|
38
38
|
empuno leave-requests.reject 124 --rejection_reason "Yoğun dönem"
|
|
39
39
|
empuno employees.documents.store 45 --document_type is_sozlesmesi --file file=./sozlesme.pdf
|
|
40
40
|
empuno leave-reports.download 12 --output ./rapor.pdf
|
|
41
|
+
empuno reference sgk_exit_codes # bir alanın geçerli değerleri
|
|
41
42
|
empuno call POST /manager/leave-requests/123/approve # ham çağrı
|
|
42
43
|
empuno api leaves --status approved # salt-okuma Company API
|
|
43
44
|
```
|
|
@@ -50,6 +51,9 @@ komutu CLI güncellemesi olmadan görünür.
|
|
|
50
51
|
- `--alan @dosya` ya da `--file alan=dosya` → multipart yükleme.
|
|
51
52
|
- `--json '{…}'` / `--json @govde.json`, `--dry-run`, `--output yol`,
|
|
52
53
|
`--profile ad` (birden çok firma).
|
|
54
|
+
- Listeler `--per_page` (≤100) ve `--page` alır; yeni kaydın kimliği yanıtta
|
|
55
|
+
`flash.created` ya da `redirect.params` içindedir.
|
|
56
|
+
- Geçmiş veri aktarımı (`data-migration.*`) davet/e-posta/bildirim göndermez.
|
|
53
57
|
|
|
54
58
|
## Talepler (empuno ekibine)
|
|
55
59
|
|
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.3.0'; // 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;
|
|
@@ -234,7 +234,7 @@ function compareVersions(a, b) {
|
|
|
234
234
|
return 0;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
async function request(auth, method, urlPath, { query = {}, body = null, files = [], output = null, dryRun = false } = {}) {
|
|
237
|
+
async function request(auth, method, urlPath, { query = {}, body = null, files = [], output = null, dryRun = false, headers: extraHeaders = {} } = {}) {
|
|
238
238
|
const url = new URL(urlPath.startsWith('http') ? urlPath : auth.url + urlPath);
|
|
239
239
|
for (const [k, v] of Object.entries(query)) {
|
|
240
240
|
if (Array.isArray(v)) v.forEach((item) => url.searchParams.append(`${k}[]`, String(item)));
|
|
@@ -246,6 +246,7 @@ async function request(auth, method, urlPath, { query = {}, body = null, files =
|
|
|
246
246
|
Accept: 'application/json',
|
|
247
247
|
'User-Agent': `empuno-cli/${VERSION} node/${process.versions.node}`,
|
|
248
248
|
'X-Empuno-Cli': VERSION,
|
|
249
|
+
...extraHeaders,
|
|
249
250
|
};
|
|
250
251
|
|
|
251
252
|
let sendMethod = method;
|
|
@@ -647,6 +648,23 @@ function installedViaNpm() {
|
|
|
647
648
|
}
|
|
648
649
|
}
|
|
649
650
|
|
|
651
|
+
async function cmdReference(args) {
|
|
652
|
+
const auth = resolveAuth(args.flags);
|
|
653
|
+
requireKey(auth);
|
|
654
|
+
const name = args.positional[0];
|
|
655
|
+
const { ok, result } = await request(auth, 'GET', `/api/company/v1/reference${name ? `/${encodeURIComponent(name)}` : ''}`);
|
|
656
|
+
if (wantsText() && ok && name && Array.isArray(result?.data)) {
|
|
657
|
+
for (const row of result.data) {
|
|
658
|
+
const key = row.code ?? row.value ?? row.id;
|
|
659
|
+
const label = row.label ?? row.name ?? '';
|
|
660
|
+
process.stdout.write(`${String(key).padEnd(24)} ${label}\n`);
|
|
661
|
+
}
|
|
662
|
+
return 0;
|
|
663
|
+
}
|
|
664
|
+
print(result);
|
|
665
|
+
return ok ? 0 : 1;
|
|
666
|
+
}
|
|
667
|
+
|
|
650
668
|
async function cmdUpdate(args) {
|
|
651
669
|
if (installedViaNpm()) {
|
|
652
670
|
// npm'in yönettiği dosyanın üzerine yazmak sürüm kaydını bozar.
|
|
@@ -760,8 +778,8 @@ async function cmdTickets(args) {
|
|
|
760
778
|
const auth = resolveAuth(flags);
|
|
761
779
|
requireKey(auth);
|
|
762
780
|
|
|
763
|
-
const send = async (method, urlPath, body = null, query = {}) => {
|
|
764
|
-
const response = await request(auth, method, urlPath, { body, query, dryRun: flags['dry-run'] });
|
|
781
|
+
const send = async (method, urlPath, body = null, query = {}, headers = {}) => {
|
|
782
|
+
const response = await request(auth, method, urlPath, { body, query, headers, dryRun: flags['dry-run'] });
|
|
765
783
|
return response;
|
|
766
784
|
};
|
|
767
785
|
|
|
@@ -789,7 +807,10 @@ async function cmdTickets(args) {
|
|
|
789
807
|
}
|
|
790
808
|
let context = opts.context ? readJsonArg(String(opts.context)) : {};
|
|
791
809
|
if (opts.command) context = { ...context, command: String(opts.command) };
|
|
792
|
-
|
|
810
|
+
// Aynı içerik → aynı anahtar: betik yeniden denese de ikinci talep açılmaz
|
|
811
|
+
// (sunucu mevcut talebi duplicate=true ile döner).
|
|
812
|
+
const idem = crypto.createHash('sha256').update(`${title}|${kind}|${body}`).digest('hex').slice(0, 40);
|
|
813
|
+
const r = await send('POST', base, { title: String(title), kind, body, priority: opts.priority, context: Object.keys(context).length ? context : undefined }, {}, { 'Idempotency-Key': opts['idempotency-key'] ? String(opts['idempotency-key']) : idem });
|
|
793
814
|
if (r.dryRun) return print(r.result), 0;
|
|
794
815
|
print(r.result);
|
|
795
816
|
return r.ok ? 0 : 1;
|
|
@@ -819,6 +840,140 @@ async function cmdTickets(args) {
|
|
|
819
840
|
}
|
|
820
841
|
}
|
|
821
842
|
|
|
843
|
+
// ─── geçiş (başka sistemden geçmiş verinin aktarımı) ─────────────────────────
|
|
844
|
+
|
|
845
|
+
const MIGRATE_KINDS = {
|
|
846
|
+
employees: { path: '/manager/data-migration/employees/commit', chunk: 200 },
|
|
847
|
+
leaves: { path: '/manager/data-migration/leaves/commit', chunk: 500 },
|
|
848
|
+
advances: { path: '/manager/data-migration/advances/commit', chunk: 200 },
|
|
849
|
+
expenses: { path: '/manager/data-migration/expenses/commit', chunk: 200 },
|
|
850
|
+
documents: { path: '/manager/data-migration/documents/commit', chunk: 10 },
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
/** Basit CSV ayrıştırıcı: tırnak, "" kaçışı, ; ya da , ayırıcı (başlıktan seçilir), BOM. */
|
|
854
|
+
function parseCsv(text) {
|
|
855
|
+
const src = text.replace(/^/, '');
|
|
856
|
+
const firstLine = src.split(/\r?\n/, 1)[0] || '';
|
|
857
|
+
const delim = (firstLine.match(/;/g) || []).length > (firstLine.match(/,/g) || []).length ? ';' : ',';
|
|
858
|
+
const rows = [];
|
|
859
|
+
let row = [];
|
|
860
|
+
let field = '';
|
|
861
|
+
let quoted = false;
|
|
862
|
+
for (let i = 0; i < src.length; i++) {
|
|
863
|
+
const c = src[i];
|
|
864
|
+
if (quoted) {
|
|
865
|
+
if (c === '"' && src[i + 1] === '"') { field += '"'; i++; }
|
|
866
|
+
else if (c === '"') quoted = false;
|
|
867
|
+
else field += c;
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (c === '"') quoted = true;
|
|
871
|
+
else if (c === delim) { row.push(field); field = ''; }
|
|
872
|
+
else if (c === '\n' || c === '\r') {
|
|
873
|
+
if (c === '\r' && src[i + 1] === '\n') i++;
|
|
874
|
+
row.push(field); field = '';
|
|
875
|
+
if (row.some((v) => v !== '')) rows.push(row);
|
|
876
|
+
row = [];
|
|
877
|
+
} else field += c;
|
|
878
|
+
}
|
|
879
|
+
row.push(field);
|
|
880
|
+
if (row.some((v) => v !== '')) rows.push(row);
|
|
881
|
+
const [header, ...data] = rows;
|
|
882
|
+
if (!header) return [];
|
|
883
|
+
const keys = header.map((h) => h.trim());
|
|
884
|
+
return data.map((cells) => {
|
|
885
|
+
const obj = {};
|
|
886
|
+
keys.forEach((k, idx) => {
|
|
887
|
+
const v = (cells[idx] ?? '').trim();
|
|
888
|
+
if (v === '' || !k) return;
|
|
889
|
+
// CSV değerleri metin kalır (TC, sicil no, telefon); yalnız mantıksal değerler çevrilir.
|
|
890
|
+
obj[k] = ['true', 'false'].includes(v.toLowerCase()) ? v.toLowerCase() === 'true' : v;
|
|
891
|
+
});
|
|
892
|
+
return obj;
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function readRecordsFile(file) {
|
|
897
|
+
if (!fs.existsSync(file)) throw new UsageError(`dosya bulunamadı: ${file}`);
|
|
898
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
899
|
+
if (/\.csv$/i.test(file)) return parseCsv(text);
|
|
900
|
+
let data;
|
|
901
|
+
try { data = JSON.parse(text); } catch (e) { throw new UsageError(`geçersiz JSON (${file}): ${e.message}`); }
|
|
902
|
+
const records = Array.isArray(data) ? data : data?.records;
|
|
903
|
+
if (!Array.isArray(records)) throw new UsageError('JSON bir dizi ya da {"records": [...]} olmalı');
|
|
904
|
+
return records;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
async function cmdMigrate(args) {
|
|
908
|
+
const { flags, fields, positional } = args;
|
|
909
|
+
const [kind, file] = positional;
|
|
910
|
+
if (!kind || !MIGRATE_KINDS[kind] || !file) {
|
|
911
|
+
throw new UsageError(`kullanım: empuno migrate <${Object.keys(MIGRATE_KINDS).join('|')}> <kayitlar.json|.csv> [--validate] [--on_existing update] [--employee_id N] [--chunk N]
|
|
912
|
+
alanlar: empuno describe data-migration.${kind && MIGRATE_KINDS[kind] ? kind : 'employees'}.commit · rehber: https://empuno.com/api-docs/agent#gecis`);
|
|
913
|
+
}
|
|
914
|
+
const auth = resolveAuth(flags);
|
|
915
|
+
requireKey(auth);
|
|
916
|
+
const opts = fieldMap(fields);
|
|
917
|
+
const spec = MIGRATE_KINDS[kind];
|
|
918
|
+
const records = readRecordsFile(file);
|
|
919
|
+
const chunkSize = Math.max(1, Math.min(Number(opts.chunk) || spec.chunk, spec.chunk));
|
|
920
|
+
const validate = Boolean(flags.validate);
|
|
921
|
+
const baseDir = path.dirname(path.resolve(file));
|
|
922
|
+
|
|
923
|
+
const summary = { kind, file, dry_run: validate, records: records.length, created: 0, updated: 0, skipped: 0, failed: 0, results: [] };
|
|
924
|
+
|
|
925
|
+
for (let offset = 0; offset < records.length; offset += chunkSize) {
|
|
926
|
+
const chunk = records.slice(offset, offset + chunkSize);
|
|
927
|
+
let response;
|
|
928
|
+
|
|
929
|
+
if (kind === 'documents') {
|
|
930
|
+
const files = [];
|
|
931
|
+
const manifest = chunk.map((entry, i) => {
|
|
932
|
+
const filePath = path.resolve(baseDir, String(entry.file ?? ''));
|
|
933
|
+
files.push({ field: 'files[]', file: filePath });
|
|
934
|
+
return { ...entry, file: i };
|
|
935
|
+
});
|
|
936
|
+
for (const f of files) if (!fs.existsSync(f.file)) throw new UsageError(`dosya bulunamadı: ${f.file}`);
|
|
937
|
+
response = await request(auth, 'POST', spec.path, {
|
|
938
|
+
body: { manifest: JSON.stringify(manifest), ...(validate ? { dry_run: 1 } : {}) },
|
|
939
|
+
files,
|
|
940
|
+
dryRun: flags['dry-run'],
|
|
941
|
+
});
|
|
942
|
+
} else {
|
|
943
|
+
const body = { records: chunk, ...(validate ? { dry_run: true } : {}) };
|
|
944
|
+
if (opts.on_existing) body.on_existing = String(opts.on_existing);
|
|
945
|
+
if (opts.employee_id) body.employee_id = Number(opts.employee_id);
|
|
946
|
+
response = await request(auth, 'POST', spec.path, { body, dryRun: flags['dry-run'] });
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (response.dryRun) {
|
|
950
|
+
print(response.result);
|
|
951
|
+
return 0;
|
|
952
|
+
}
|
|
953
|
+
const r = response.result || {};
|
|
954
|
+
if (!response.ok) {
|
|
955
|
+
// Doğrulama hatası (422) tüm parçayı etkiler: sunucunun errors'ını kayıt indeksine çevir.
|
|
956
|
+
summary.failed += chunk.length;
|
|
957
|
+
summary.results.push({ chunk_offset: offset, action: 'failed', status: response.status, message: r.message, errors: r.errors });
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
for (const key of ['created', 'updated', 'skipped', 'failed']) summary[key] += Number(r[key] || 0);
|
|
961
|
+
for (const row of r.results || []) summary.results.push({ ...row, index: offset + Number(row.index ?? 0) });
|
|
962
|
+
if (isTTY) warn(`${Math.min(offset + chunkSize, records.length)}/${records.length} işlendi`);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
summary.message = `${summary.created} ${validate ? 'eklenecek' : 'eklendi'}, ${summary.updated} güncellen${validate ? 'ecek' : 'di'}, ${summary.skipped} atlandı, ${summary.failed} hatalı${validate ? ' (doğrulama — hiçbir şey yazılmadı)' : ''}`;
|
|
966
|
+
if (wantsText()) {
|
|
967
|
+
process.stdout.write(`${summary.message}\n`);
|
|
968
|
+
for (const row of summary.results.filter((x) => x.action === 'failed')) {
|
|
969
|
+
process.stdout.write(` ✗ #${row.index ?? row.chunk_offset}: ${row.reason || row.message || ''}${row.errors ? ' ' + JSON.stringify(row.errors) : ''}\n`);
|
|
970
|
+
}
|
|
971
|
+
} else {
|
|
972
|
+
print(summary);
|
|
973
|
+
}
|
|
974
|
+
return summary.failed > 0 ? 1 : 0;
|
|
975
|
+
}
|
|
976
|
+
|
|
822
977
|
// ─── destek (yalnız empuno ekibinin ajanları — emp_staff_ jetonu) ────────────
|
|
823
978
|
|
|
824
979
|
function requireStaff(auth) {
|
|
@@ -928,7 +1083,8 @@ KURULUM
|
|
|
928
1083
|
KEŞİF
|
|
929
1084
|
empuno whoami # firma, anahtar türü, kimin adına çalıştığı
|
|
930
1085
|
empuno commands [ara] [--module leaves] [--writes|--reads] [--all] [--full]
|
|
931
|
-
empuno describe <komut> # yöntem, yol, yol parametreleri, alanlar/kurallar, gereken yetki
|
|
1086
|
+
empuno describe <komut> # yöntem, yol, yol parametreleri, alanlar/kurallar, gereken yetki, hız sınırı
|
|
1087
|
+
empuno reference [ad] # geçerli değerler: sgk_exit_codes, employee_document_types, departments, branches, leave_types…
|
|
932
1088
|
|
|
933
1089
|
ÇALIŞTIRMA (komut = panel rotası; listeden)
|
|
934
1090
|
empuno leave-requests.index --status pending --per_page 50
|
|
@@ -946,6 +1102,15 @@ ALANLAR
|
|
|
946
1102
|
--alan @./dosya.pdf | --file alan=./dosya.pdf # multipart yükleme
|
|
947
1103
|
--query anahtar=değer # yazma isteğinde ek sorgu parametresi
|
|
948
1104
|
|
|
1105
|
+
GEÇİŞ (başka sistemden geçmiş veri — JSON ya da CSV; davet/e-posta/bildirim gitmez; tekrar çalıştırmak güvenli)
|
|
1106
|
+
empuno migrate employees calisanlar.csv --validate # önce yazmadan doğrula
|
|
1107
|
+
empuno migrate employees calisanlar.csv [--on_existing update] # ayrılmışlar dahil; TC ile eşler
|
|
1108
|
+
empuno migrate leaves izinler.json # her satırda identity_number|employee_number + leave_type
|
|
1109
|
+
empuno migrate advances avanslar.csv # geçmiş avans + devreden bakiye (deducted_amount)
|
|
1110
|
+
empuno migrate expenses masraflar.csv # ödenmiş geçmiş masraf (kategori adla)
|
|
1111
|
+
empuno migrate documents manifest.csv # file sütunu: dosya yolu; ≤10'arlı gönderilir
|
|
1112
|
+
alanlar: empuno describe data-migration.<tür>.commit · sıra: çalışanlar → izin → avans → masraf → evrak
|
|
1113
|
+
|
|
949
1114
|
TALEPLER (geliştirme isteği, hata, eleştiri, soru → empuno ekibi)
|
|
950
1115
|
empuno tickets [--status active|open|answered|closed|all] [--unread] [--mine]
|
|
951
1116
|
empuno tickets open "Toplu izin onayı" --kind feature --body "…"|@not.md [--priority high] [--command leave-requests.approve]
|
|
@@ -972,8 +1137,9 @@ BELGE: https://empuno.com/api-docs/agent
|
|
|
972
1137
|
|
|
973
1138
|
async function main() {
|
|
974
1139
|
const argv = process.argv.slice(2);
|
|
975
|
-
const
|
|
976
|
-
const
|
|
1140
|
+
const sub0 = argv.find((a) => !a.startsWith('--')) ?? '';
|
|
1141
|
+
const ticketish = ['tickets', 'ticket', 'support'].includes(sub0);
|
|
1142
|
+
const args = parseArgs(argv, ticketish ? ['mine', 'unread', 'close', 'token-stdin'] : sub0 === 'migrate' ? ['validate'] : []);
|
|
977
1143
|
const [sub, ...rest] = args.positional;
|
|
978
1144
|
args.positional = rest;
|
|
979
1145
|
|
|
@@ -982,7 +1148,7 @@ async function main() {
|
|
|
982
1148
|
return 0;
|
|
983
1149
|
}
|
|
984
1150
|
if (args.flags.help) {
|
|
985
|
-
if (['tickets', 'ticket', 'support', 'login', 'commands', 'describe', 'call', 'api'].includes(sub)) {
|
|
1151
|
+
if (['tickets', 'ticket', 'support', 'login', 'commands', 'describe', 'call', 'api', 'migrate', 'reference'].includes(sub)) {
|
|
986
1152
|
process.stdout.write(HELP);
|
|
987
1153
|
return 0;
|
|
988
1154
|
}
|
|
@@ -1016,6 +1182,11 @@ async function main() {
|
|
|
1016
1182
|
case 'tickets':
|
|
1017
1183
|
case 'ticket':
|
|
1018
1184
|
return cmdTickets(args);
|
|
1185
|
+
case 'reference':
|
|
1186
|
+
case 'ref':
|
|
1187
|
+
return cmdReference(args);
|
|
1188
|
+
case 'migrate':
|
|
1189
|
+
return cmdMigrate(args);
|
|
1019
1190
|
case 'support':
|
|
1020
1191
|
return cmdSupport(args);
|
|
1021
1192
|
case 'run':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "empuno",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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": {
|