@newpeak/barista-cli 0.2.196 → 0.2.199

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.
@@ -11505,7 +11505,12 @@ export const apiClient = {
11505
11505
  return extractApiError(error);
11506
11506
  }
11507
11507
  },
11508
- async uploadDocument(environment, tenant, filePath, categoryCode, businessData, remark, timeout) {
11508
+ /// ========== ChunkedFileController single file upload ==========
11509
+ /**
11510
+ * Upload a file via ChunkedFileController (/enterprise/files/upload).
11511
+ * X-TENANT-ID is injected by nginx — no client header needed.
11512
+ */
11513
+ async uploadFileToStorage(environment, tenant, filePath, timeout) {
11509
11514
  try {
11510
11515
  const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11511
11516
  if (!token)
@@ -11517,23 +11522,13 @@ export const apiClient = {
11517
11522
  const FormData = (await import('form-data')).default;
11518
11523
  const formData = new FormData();
11519
11524
  formData.append('file', createReadStream(filePath), basename(filePath));
11520
- if (categoryCode)
11521
- formData.append('categoryCode', categoryCode);
11522
- if (businessData)
11523
- formData.append('businessData', businessData);
11524
- if (remark)
11525
- formData.append('remark', remark);
11526
- const axiosConfig = {
11527
- headers: formData.getHeaders(),
11528
- };
11529
- if (timeout !== undefined) {
11525
+ const axiosConfig = { headers: formData.getHeaders() };
11526
+ if (timeout !== undefined)
11530
11527
  axiosConfig.timeout = timeout;
11531
- }
11532
- const response = await client.getClient().post('/api/enterprise/document/upload', formData, axiosConfig);
11528
+ const response = await client.getClient().post('/api/enterprise/files/upload', formData, axiosConfig);
11533
11529
  const body = response.data;
11534
- if (body && body.code === '00000') {
11530
+ if (body && body.code === '00000')
11535
11531
  return { success: true, data: body.data };
11536
- }
11537
11532
  return {
11538
11533
  success: false,
11539
11534
  error: { code: body?.code || 'UNKNOWN', message: JSON.stringify(body?.data) || 'Upload failed' },
@@ -11543,40 +11538,215 @@ export const apiClient = {
11543
11538
  return extractApiError(error);
11544
11539
  }
11545
11540
  },
11546
- async uploadSysFile(environment, tenant, filePath, secretFlag = 'N', timeout) {
11541
+ /// ========== DocumentController save document metadata ==========
11542
+ /**
11543
+ * Save a document record associating an uploaded file with a category.
11544
+ * POST /api/enterprise/document/save
11545
+ */
11546
+ async saveDocument(environment, tenant, params, timeout) {
11547
11547
  try {
11548
11548
  const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11549
11549
  if (!token)
11550
11550
  return { success: false, error: { code: 'NO_TOKEN', message: 'Not logged in' } };
11551
11551
  const client = createAPIClient('liberica', environment, tenant);
11552
11552
  client.setAuthToken(token);
11553
- const { createReadStream } = await import('node:fs');
11554
- const { basename } = await import('node:path');
11555
- const FormData = (await import('form-data')).default;
11556
- const formData = new FormData();
11557
- formData.append('file', createReadStream(filePath), basename(filePath));
11558
- formData.append('secretFlag', secretFlag);
11559
- const axiosConfig = {
11560
- headers: formData.getHeaders(),
11553
+ const payload = {
11554
+ fileId: params.fileId, fileName: params.fileName, fileSize: params.fileSize,
11561
11555
  };
11562
- if (timeout !== undefined) {
11556
+ if (params.fileType)
11557
+ payload.fileType = params.fileType;
11558
+ if (params.categoryCode)
11559
+ payload.categoryCode = params.categoryCode;
11560
+ if (params.businessData)
11561
+ payload.businessData = params.businessData;
11562
+ if (params.remark)
11563
+ payload.remark = params.remark;
11564
+ const axiosConfig = { headers: { 'Content-Type': 'application/json' } };
11565
+ if (timeout !== undefined)
11563
11566
  axiosConfig.timeout = timeout;
11564
- }
11565
- const response = await client.getClient().post('/api/sysFileInfo/upload', formData, axiosConfig);
11567
+ const response = await client.getClient().post('/api/enterprise/document/save', payload, axiosConfig);
11566
11568
  const body = response.data;
11567
- // Roses framework returns { code, data } — normalize to CLI's { success, data }
11568
- if (body && body.code === '00000') {
11569
+ if (body && body.code === '00000')
11569
11570
  return { success: true, data: body.data };
11570
- }
11571
11571
  return {
11572
11572
  success: false,
11573
- error: { code: body?.code || 'UNKNOWN', message: JSON.stringify(body?.data) || 'Upload failed' },
11573
+ error: { code: body?.code || 'UNKNOWN', message: JSON.stringify(body?.data) || 'Save document failed' },
11574
11574
  };
11575
11575
  }
11576
11576
  catch (error) {
11577
11577
  return extractApiError(error);
11578
11578
  }
11579
11579
  },
11580
+ /// ========== Two-step document upload (upload file + save document metadata) ==========
11581
+ async uploadDocument(environment, tenant, filePath, categoryCode, businessData, remark, timeout) {
11582
+ const uploadResult = await this.uploadFileToStorage(environment, tenant, filePath, timeout);
11583
+ if (!uploadResult.success)
11584
+ return uploadResult;
11585
+ const { fileId, fileName, fileSize } = uploadResult.data;
11586
+ const fileSuffix = fileName.includes('.') ? fileName.split('.').pop() : undefined;
11587
+ return this.saveDocument(environment, tenant, {
11588
+ fileId, fileName, fileSize, fileType: fileSuffix,
11589
+ categoryCode, businessData, remark,
11590
+ }, timeout);
11591
+ },
11592
+ /// ========== Simple file upload (via ChunkedFileController, backward compat) ==========
11593
+ async uploadSysFile(environment, tenant, filePath, _secretFlag = 'N', timeout) {
11594
+ return this.uploadFileToStorage(environment, tenant, filePath, timeout);
11595
+ },
11596
+ /// ========== Chunked upload ==========
11597
+ async initChunkUpload(environment, tenant, fileName, fileSize, chunkSize) {
11598
+ try {
11599
+ const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11600
+ if (!token)
11601
+ return { success: false, error: { code: 'NO_TOKEN', message: 'Not logged in' } };
11602
+ const client = createAPIClient('liberica', environment, tenant);
11603
+ client.setAuthToken(token);
11604
+ const params = new URLSearchParams({ fileName, fileSize: String(fileSize) });
11605
+ if (chunkSize)
11606
+ params.append('chunkSize', String(chunkSize));
11607
+ const response = await client.getClient().post(`/api/enterprise/files/chunk/init?${params.toString()}`, null);
11608
+ const body = response.data;
11609
+ if (body && body.code === '00000')
11610
+ return { success: true, data: body.data };
11611
+ return { success: false, error: { code: body?.code || 'UNKNOWN', message: 'Init chunk upload failed' } };
11612
+ }
11613
+ catch (error) {
11614
+ return extractApiError(error);
11615
+ }
11616
+ },
11617
+ async uploadChunk(environment, tenant, uploadId, index, chunkFilePath, timeout) {
11618
+ try {
11619
+ const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11620
+ if (!token)
11621
+ return { success: false, error: { code: 'NO_TOKEN', message: 'Not logged in' } };
11622
+ const client = createAPIClient('liberica', environment, tenant);
11623
+ client.setAuthToken(token);
11624
+ const { createReadStream } = await import('node:fs');
11625
+ const FormData = (await import('form-data')).default;
11626
+ const formData = new FormData();
11627
+ formData.append('uploadId', uploadId);
11628
+ formData.append('index', String(index));
11629
+ formData.append('file', createReadStream(chunkFilePath), `chunk-${index}`);
11630
+ const axiosConfig = { headers: formData.getHeaders() };
11631
+ if (timeout !== undefined)
11632
+ axiosConfig.timeout = timeout;
11633
+ const response = await client.getClient().post('/api/enterprise/files/chunk/upload', formData, axiosConfig);
11634
+ const body = response.data;
11635
+ if (body && body.code === '00000')
11636
+ return { success: true, data: body.data };
11637
+ return { success: false, error: { code: body?.code || 'UNKNOWN', message: 'Chunk upload failed' } };
11638
+ }
11639
+ catch (error) {
11640
+ return extractApiError(error);
11641
+ }
11642
+ },
11643
+ async completeChunkUpload(environment, tenant, uploadId) {
11644
+ try {
11645
+ const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11646
+ if (!token)
11647
+ return { success: false, error: { code: 'NO_TOKEN', message: 'Not logged in' } };
11648
+ const client = createAPIClient('liberica', environment, tenant);
11649
+ client.setAuthToken(token);
11650
+ const response = await client.getClient().post(`/api/enterprise/files/chunk/complete?uploadId=${uploadId}`, null);
11651
+ const body = response.data;
11652
+ if (body && body.code === '00000')
11653
+ return { success: true, data: body.data };
11654
+ return { success: false, error: { code: body?.code || 'UNKNOWN', message: 'Complete chunk upload failed' } };
11655
+ }
11656
+ catch (error) {
11657
+ return extractApiError(error);
11658
+ }
11659
+ },
11660
+ async getChunkUploadStatus(environment, tenant, uploadId) {
11661
+ try {
11662
+ const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
11663
+ if (!token)
11664
+ return { success: false, error: { code: 'NO_TOKEN', message: 'Not logged in' } };
11665
+ const client = createAPIClient('liberica', environment, tenant);
11666
+ client.setAuthToken(token);
11667
+ const response = await client.getClient().get(`/api/enterprise/files/chunk/complete/status?uploadId=${uploadId}`);
11668
+ const body = response.data;
11669
+ if (body && body.code === '00000')
11670
+ return { success: true, data: body.data };
11671
+ return { success: false, error: { code: body?.code || 'UNKNOWN', message: 'Get chunk status failed' } };
11672
+ }
11673
+ catch (error) {
11674
+ return extractApiError(error);
11675
+ }
11676
+ },
11677
+ /**
11678
+ * Complete chunked upload flow: init → upload all chunks → complete → poll until COMPLETED.
11679
+ * Max poll time: 10 minutes (600s).
11680
+ */
11681
+ async uploadFileChunked(environment, tenant, filePath, timeout) {
11682
+ const { statSync, openSync, readSync, writeFileSync, closeSync, rmSync, mkdtempSync } = await import('node:fs');
11683
+ const { basename, join } = await import('node:path');
11684
+ const { tmpdir } = await import('node:os');
11685
+ const stats = statSync(filePath);
11686
+ const fileName = basename(filePath);
11687
+ const fileSize = stats.size;
11688
+ const chunkSize = 5 * 1024 * 1024;
11689
+ const initResult = await this.initChunkUpload(environment, tenant, fileName, fileSize, chunkSize);
11690
+ if (!initResult.success)
11691
+ return initResult;
11692
+ const { uploadId, chunkCount } = initResult.data;
11693
+ const chunkDir = mkdtempSync(join(tmpdir(), 'barista-chunk-'));
11694
+ try {
11695
+ const fd = openSync(filePath, 'r');
11696
+ const buffer = Buffer.alloc(chunkSize);
11697
+ // Pre-write all chunks to temp files
11698
+ const chunkFiles = [];
11699
+ for (let i = 0; i < chunkCount; i++) {
11700
+ const bytesRead = readSync(fd, buffer, 0, chunkSize, i * chunkSize);
11701
+ const chunkFile = join(chunkDir, `chunk-${i}`);
11702
+ writeFileSync(chunkFile, buffer.subarray(0, bytesRead));
11703
+ chunkFiles.push(chunkFile);
11704
+ }
11705
+ closeSync(fd);
11706
+ // Upload chunks in batches of 3 (like Android OTG_CHUNK_CONCURRENCY)
11707
+ const CONCURRENCY = 3;
11708
+ let chunkError = null;
11709
+ for (let start = 0; start < chunkCount && !chunkError; start += CONCURRENCY) {
11710
+ const end = Math.min(start + CONCURRENCY, chunkCount);
11711
+ const promises = [];
11712
+ for (let i = start; i < end; i++) {
11713
+ const idx = i;
11714
+ promises.push(this.uploadChunk(environment, tenant, uploadId, idx, chunkFiles[idx], timeout)
11715
+ .then((r) => {
11716
+ if (!r.success && !chunkError) {
11717
+ chunkError = `Chunk ${idx + 1}/${chunkCount} upload failed: ${r.error?.message || 'unknown'}`;
11718
+ }
11719
+ }));
11720
+ }
11721
+ await Promise.all(promises);
11722
+ }
11723
+ if (chunkError) {
11724
+ return { success: false, error: { code: 'CHUNK_UPLOAD_FAILED', message: chunkError } };
11725
+ }
11726
+ const completeResult = await this.completeChunkUpload(environment, tenant, uploadId);
11727
+ if (!completeResult.success)
11728
+ return completeResult;
11729
+ const maxAttempts = 60;
11730
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
11731
+ await new Promise(r => setTimeout(r, 10000));
11732
+ const statusResult = await this.getChunkUploadStatus(environment, tenant, uploadId);
11733
+ if (!statusResult.success)
11734
+ continue;
11735
+ const s = statusResult.data;
11736
+ if (s.status === 'COMPLETED')
11737
+ return { success: true, data: { fileId: s.fileId, fileName: s.fileName || fileName, fileSize: s.fileSize || fileSize } };
11738
+ if (s.status === 'FAILED')
11739
+ return { success: false, error: { code: 'CHUNK_UPLOAD_FAILED', message: s.errorMessage || 'Chunk upload failed' } };
11740
+ }
11741
+ return { success: false, error: { code: 'CHUNK_UPLOAD_TIMEOUT', message: 'Chunk upload timed out after 10 minutes' } };
11742
+ }
11743
+ finally {
11744
+ try {
11745
+ rmSync(chunkDir, { recursive: true, force: true });
11746
+ }
11747
+ catch { /* cleanup best-effort */ }
11748
+ }
11749
+ },
11580
11750
  async getHrsSkillDefinitions(environment, tenant) {
11581
11751
  try {
11582
11752
  const token = await tokenManager.getToken({ service: 'liberica', environment, tenant });
@@ -13551,7 +13721,7 @@ export const apiClient = {
13551
13721
  }
13552
13722
  const client = createAPIClient('liberica', environment, tenant);
13553
13723
  client.setAuthToken(token);
13554
- const response = await client.getClient().post('/api/enterprise/fp/estimate/add', data);
13724
+ const response = await client.getClient().post('/api/enterprise/team/issue/fpEstimate', data);
13555
13725
  return response.data;
13556
13726
  }
13557
13727
  catch (error) {
@@ -13566,7 +13736,7 @@ export const apiClient = {
13566
13736
  }
13567
13737
  const client = createAPIClient('liberica', environment, tenant);
13568
13738
  client.setAuthToken(token);
13569
- const response = await client.getClient().post('/api/enterprise/fp/actual/add', data);
13739
+ const response = await client.getClient().post('/api/enterprise/team/issue/fpActual', data);
13570
13740
  return response.data;
13571
13741
  }
13572
13742
  catch (error) {
@@ -13581,7 +13751,7 @@ export const apiClient = {
13581
13751
  }
13582
13752
  const client = createAPIClient('liberica', environment, tenant);
13583
13753
  client.setAuthToken(token);
13584
- const response = await client.getClient().post('/api/enterprise/fp/review', data);
13754
+ const response = await client.getClient().post('/api/enterprise/team/issue/fpReview', data);
13585
13755
  return response.data;
13586
13756
  }
13587
13757
  catch (error) {
@@ -13605,7 +13775,7 @@ export const apiClient = {
13605
13775
  queryString.append('endDate', params.endDate);
13606
13776
  if (params?.methodType)
13607
13777
  queryString.append('methodType', params.methodType);
13608
- const response = await client.getClient().get(`/api/enterprise/fp/summary?${queryString.toString()}`);
13778
+ const response = await client.getClient().get(`/api/enterprise/team/issue/fpSummary?${queryString.toString()}`);
13609
13779
  return response.data;
13610
13780
  }
13611
13781
  catch (error) {
@@ -13633,7 +13803,7 @@ export const apiClient = {
13633
13803
  queryString.append('includeTimeBuckets', String(params.includeTimeBuckets));
13634
13804
  if (params?.includeLoadCheck)
13635
13805
  queryString.append('includeLoadCheck', String(params.includeLoadCheck));
13636
- const response = await client.getClient().get(`/api/enterprise/fp/stats?${queryString.toString()}`);
13806
+ const response = await client.getClient().get(`/api/enterprise/team/issue/fpStats?${queryString.toString()}`);
13637
13807
  return response.data;
13638
13808
  }
13639
13809
  catch (error) {