@carthooks/arcubase-cli 0.1.12 → 0.1.15

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.
Files changed (46) hide show
  1. package/bundle/arcubase-admin.mjs +15019 -3802
  2. package/bundle/arcubase.mjs +15019 -3802
  3. package/dist/generated/command_registry.generated.d.ts +108 -0
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +145 -0
  6. package/dist/generated/zod_registry.generated.d.ts +23 -23
  7. package/dist/runtime/entity_import_mapping.d.ts +12 -0
  8. package/dist/runtime/entity_import_mapping.d.ts.map +1 -0
  9. package/dist/runtime/entity_import_mapping.js +133 -0
  10. package/dist/runtime/entity_save_schema.d.ts.map +1 -1
  11. package/dist/runtime/entity_save_schema.js +23 -2
  12. package/dist/runtime/errors.d.ts +1 -0
  13. package/dist/runtime/errors.d.ts.map +1 -1
  14. package/dist/runtime/execute.d.ts.map +1 -1
  15. package/dist/runtime/execute.js +414 -8
  16. package/dist/runtime/local_upload.d.ts +10 -0
  17. package/dist/runtime/local_upload.d.ts.map +1 -0
  18. package/dist/runtime/local_upload.js +73 -0
  19. package/dist/tests/entity_save_schema.test.js +38 -0
  20. package/dist/tests/execute_validation.test.js +64 -2
  21. package/dist/tests/help.test.js +2 -0
  22. package/package.json +7 -7
  23. package/sdk-dist/docs/runtime-reference/README.md +4 -0
  24. package/sdk-dist/docs/runtime-reference/access-rule.md +19 -0
  25. package/sdk-dist/docs/runtime-reference/entity-schema/textarea.md +2 -2
  26. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.query.json +1 -1
  27. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.query.json +1 -1
  28. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.query.json +1 -1
  29. package/sdk-dist/docs/runtime-reference/row-crud.md +68 -0
  30. package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +26 -1
  31. package/sdk-dist/docs/runtime-reference/table-lifecycle.md +1 -1
  32. package/sdk-dist/generated/command_registry.generated.ts +145 -0
  33. package/sdk-dist/types/common.ts +1 -0
  34. package/src/generated/command_registry.generated.ts +145 -0
  35. package/src/runtime/entity_import_mapping.ts +172 -0
  36. package/src/runtime/entity_save_schema.ts +27 -3
  37. package/src/runtime/errors.ts +1 -0
  38. package/src/runtime/execute.ts +434 -10
  39. package/src/runtime/local_upload.ts +84 -0
  40. package/src/tests/command_registry.test.ts +4 -0
  41. package/src/tests/entity_import_mapping.test.ts +87 -0
  42. package/src/tests/entity_save_schema.test.ts +40 -0
  43. package/src/tests/execute_validation.test.ts +299 -5
  44. package/src/tests/help.test.ts +16 -0
  45. package/src/tests/local_upload.test.ts +47 -0
  46. package/src/tests/zod_registry.test.ts +27 -0
@@ -61,6 +61,44 @@ test('EntitySaveReqVO normalizes mechanical FieldVO defaults', () => {
61
61
  assert.equal(parsed.data.fields[0].transfers, null);
62
62
  assert.deepEqual(parsed.data.fields[0].depends, []);
63
63
  });
64
+ test('EntitySaveReqVO rejects textarea size as string', () => {
65
+ assert.ok(schema);
66
+ const parsed = schema.safeParse({
67
+ name: '订单',
68
+ field_id_seq: 1003,
69
+ layout: [[1001], [1002]],
70
+ fields: [
71
+ buildMinimalField(1001),
72
+ {
73
+ ...buildMinimalField(1002),
74
+ label: '描述',
75
+ type: 'textarea',
76
+ key: 'description',
77
+ options: { placeholder: '', html: false, size: 'default', lengthLimit: false, lengthMin: 0, lengthMax: 2000 },
78
+ },
79
+ ],
80
+ });
81
+ assert.equal(parsed.success, false);
82
+ });
83
+ test('EntitySaveReqVO accepts textarea size as number', () => {
84
+ assert.ok(schema);
85
+ const parsed = schema.safeParse({
86
+ name: '订单',
87
+ field_id_seq: 1003,
88
+ layout: [[1001], [1002]],
89
+ fields: [
90
+ buildMinimalField(1001),
91
+ {
92
+ ...buildMinimalField(1002),
93
+ label: '描述',
94
+ type: 'textarea',
95
+ key: 'description',
96
+ options: { placeholder: '', html: false, size: 6, lengthLimit: false, lengthMin: 0, lengthMax: 2000 },
97
+ },
98
+ ],
99
+ });
100
+ assert.equal(parsed.success, true);
101
+ });
64
102
  test('EntitySaveReqVO rejects fields as object', () => {
65
103
  assert.ok(schema);
66
104
  const parsed = schema.safeParse({
@@ -250,6 +250,37 @@ test('body json parse errors include command docs and type hints', async () => {
250
250
  return true;
251
251
  });
252
252
  });
253
+ test('access-rule invalid json returns copyable create retry examples', async () => {
254
+ await assert.rejects(async () => {
255
+ await executeCLI('admin', ['access-rule', 'create', '--app-id', 'app_1', '--table-id', 'table_1', '--body-json', '{"name":"Admin full access","enabled":true,"type":"form","options":{"user_scope":[{"type":"user","id":2188889977}],"policy":{"key":"custom","actions":["view","add","edit","delete"],"all_fields_read":true,"all_fields_write":true},"list_options":{}}'], env, async () => new Response('{}', { status: 200 }));
256
+ }, (error) => {
257
+ assert.ok(error instanceof CLIError);
258
+ assert.equal(error.code, 'INVALID_BODY_JSON');
259
+ assert.equal(error.details?.requestType, 'AppIngressCreateReqVO');
260
+ assert.match(error.message, /retryToolCalls/);
261
+ const toolCall = (error.details?.retryToolCalls ?? [])[0];
262
+ assert.ok(toolCall);
263
+ assert.equal(toolCall.command, 'access-rule create');
264
+ assert.deepEqual(toolCall.args.slice(0, 5), ['--app-id', '<app_id>', '--table-id', '<table_id>', '--body-json']);
265
+ const body = JSON.parse(toolCall.args[5]);
266
+ assert.deepEqual(body, {
267
+ name: 'Admin full access',
268
+ enabled: true,
269
+ type: 'form',
270
+ options: {
271
+ user_scope: [{ type: 'user', id: 2188889977 }],
272
+ policy: {
273
+ key: 'custom',
274
+ actions: ['view', 'add', 'edit', 'delete'],
275
+ all_fields_read: true,
276
+ all_fields_write: true,
277
+ },
278
+ list_options: {},
279
+ },
280
+ });
281
+ return true;
282
+ });
283
+ });
253
284
  test('access-rule list executes against external root path', async () => {
254
285
  const out = await executeCLI('admin', ['access-rule', 'list', '--app-id', 'app_1', '--table-id', 'table_1'], env, async (url, init) => new Response(JSON.stringify({ url, method: init?.method }), { status: 200 }));
255
286
  assert.equal(out.kind, 'result');
@@ -306,7 +337,7 @@ test('table update-schema rejects per-user permission patches', async () => {
306
337
  layout: [[1001], [1002]],
307
338
  fields: [
308
339
  { id: 1001, label: 'Title', type: 'text', required: true, unique: false, editable: true, visible: true, show_label: true, scannable: false, default_value_mode: 0, description: '', value: null, number_decimal: 0, depends: [], key: 'title', code_index: false, options: { type: 'text', placeholder: '', lengthLimit: false, lengthMin: 0, lengthMax: 255 }, transfers: null, children: null },
309
- { id: 1002, label: 'Description', type: 'textarea', required: false, unique: false, editable: true, visible: true, show_label: true, scannable: false, default_value_mode: 0, description: '', value: null, number_decimal: 0, depends: [], key: 'description', code_index: false, options: { placeholder: '', html: false, size: 'default', lengthLimit: false, lengthMin: 0, lengthMax: 2000 }, transfers: null, children: null, permission: { user: { read: false } } },
340
+ { id: 1002, label: 'Description', type: 'textarea', required: false, unique: false, editable: true, visible: true, show_label: true, scannable: false, default_value_mode: 0, description: '', value: null, number_decimal: 0, depends: [], key: 'description', code_index: false, options: { placeholder: '', html: false, size: 6, lengthLimit: false, lengthMin: 0, lengthMax: 2000 }, transfers: null, children: null, permission: { user: { read: false } } },
310
341
  ],
311
342
  options: { Buttons: [], CustomActions: [], FrontendEvents: [], TitleFormat: { Fields: [], Parts: [], Body: '' }, misc: {} },
312
343
  workflow_enabled: false,
@@ -396,6 +427,37 @@ test('row get resolves internal policy_id as query parameter', async () => {
396
427
  assert.equal(out.kind, 'result');
397
428
  assert.equal(calls[0], 'https://arcubase.example.com/api/entity/2188900691/2188900694/row/4000000046?policy_id=policy_hash_1');
398
429
  });
430
+ test('row get returns machine-readable row value evidence', async () => {
431
+ const out = await executeCLI('user', ['row', 'get', '--app-id', '2188900691', '--table-id', '2188900694', '--row-id', '4000000046'], env, withRowPolicyResolver('2188900694', ['view'], async () => new Response(JSON.stringify({
432
+ data: {
433
+ record: {
434
+ id: 4000000046,
435
+ fields: { 1001: '复测投票' },
436
+ },
437
+ fieldPermits: {
438
+ all_fields_read: false,
439
+ fields: [
440
+ { key: ':1001', read: true, write: true, report: false },
441
+ { key: ':1002', read: false, write: false, report: false },
442
+ { key: ':1003', read: true, write: true, report: false },
443
+ ],
444
+ },
445
+ },
446
+ }), { status: 200 })));
447
+ assert.deepEqual(out.rowValueEvidence, {
448
+ source: 'row get record.fields',
449
+ returnedFieldIds: ['1001'],
450
+ notReturnedFieldIds: ['1002', '1003'],
451
+ deniedReadFieldIds: ['1002'],
452
+ absentFieldMeaning: 'not returned to current user; no row-value evidence',
453
+ rules: [
454
+ 'current row values come only from row query fields or row get record.fields',
455
+ 'table schema fields[].value and default_value_mode are schema defaults, not returned row values',
456
+ 'a field id absent from returned row fields has no row-value evidence',
457
+ 'read visibility status is fieldPermits read:false; FIELD_PERMISSION_REQUIRED is only a write preflight error',
458
+ ],
459
+ });
460
+ });
399
461
  test('row command rejects user-provided policy_id', async () => {
400
462
  const bodyFile = tempJSONFile({ fields: { 1001: '今天吃什么' }, policy_id: 'manual_policy' });
401
463
  await assert.rejects(async () => {
@@ -562,7 +624,7 @@ test('row query unknown limit flag gives body-json retry path', async () => {
562
624
  assert.ok(error instanceof CLIError);
563
625
  assert.equal(error.code, 'UNKNOWN_FLAG');
564
626
  assert.equal(error.details?.requestType, 'EntityQueryReqVO');
565
- assert.deepEqual(error.details?.shapeHint, { limit: 20, offset: 0, search: { text_search: 'SO-1001' } });
627
+ assert.deepEqual(error.details?.shapeHint, { limit: 20, offset: 0, search: { q: 'SO-1001' }, sorts: [{ column: ':1001', order: 'asc' }] });
566
628
  assert.ok(error.details?.suggestions?.some((item) => item.includes('--body-json')));
567
629
  return true;
568
630
  });
@@ -81,6 +81,8 @@ test('admin access-rule help gives update whitelist and assign-users body-json e
81
81
  assert.equal(create.kind, 'help');
82
82
  assert.match(create.text, /Access rule/);
83
83
  assert.match(create.text, /access-rule\.md/);
84
+ assert.match(create.text, /full access/);
85
+ assert.match(create.text, /"actions":\["view","add","edit","delete"\]/);
84
86
  assert.match(create.text, /"user_scope":\[\{"type":"user","id":2188889901\}\]/);
85
87
  assert.doesNotMatch(create.text, /"user_scope":\[\{"type":"member"/);
86
88
  const update = await executeCLI('admin', ['access-rule', 'update', '--help'], env, async () => new Response('{}'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carthooks/arcubase-cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.15",
4
4
  "description": "Arcubase runtime CLI for admin and user command execution",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,13 +31,13 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@carthooks/client-ts": "0.8.0",
34
- "zod": "^3.25.76"
34
+ "zod": "^4.4.3"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "^20.19.0",
38
- "esbuild": "^0.25.11",
39
- "ts-morph": "^25.0.1",
40
- "tsx": "^4.20.6",
41
- "typescript": "^5.3.3"
37
+ "@types/node": "^25.9.2",
38
+ "esbuild": "^0.28.0",
39
+ "ts-morph": "^28.0.0",
40
+ "tsx": "^4.22.4",
41
+ "typescript": "^6.0.3"
42
42
  }
43
43
  }
@@ -35,12 +35,16 @@ Common commands:
35
35
 
36
36
  ```bash
37
37
  arcubase-admin app create --body-file create-app.json | --body-json <json-string>
38
+ arcubase-admin app create-from-bundle-url --url <bundle_url> [--name <app_name>]
39
+ arcubase-admin app watch-import-task --task-id <task_id> --ttl-seconds 300
38
40
  arcubase-admin table create --app-id <app_id> --body-json <json-string> | --body-file table-create.json
39
41
  arcubase-admin table get --app-id <app_id> --table-id <table_id>
40
42
  arcubase-admin table update-schema --app-id <app_id> --table-id <table_id> --body-json <json-string> | --body-file table-schema.json
41
43
  arcubase-admin access-rule assign-users --app-id <app_id> --table-id <table_id> --rule-id <rule_id> --body-json <json-string> | --body-file assign-users.json
42
44
  ```
43
45
 
46
+ `--ttl-seconds` is a local wait limit. It does not cancel the backend import task.
47
+
44
48
  Use `arcubase` for:
45
49
 
46
50
  - row insert
@@ -20,6 +20,14 @@ Use the numeric `tenantUserId` / `TenantUser.ID`.
20
20
  Do not use hash ids such as `arcubaseTenantUserId`.
21
21
  Use `user_scope[].type:"user"` for a tenant user. Do not use FieldVO field type `member` in access rules.
22
22
 
23
+ ## Body Input
24
+
25
+ ```bash
26
+ --body-json '<json-string>' | --body-file access-rule.json
27
+ ```
28
+
29
+ Prefer `--body-json` for short payloads. `--body-file` is available for large or fragile JSON payloads.
30
+
23
31
  ## Create Read/Write Rule With Hidden Field
24
32
 
25
33
  ```bash
@@ -29,6 +37,17 @@ arcubase-admin access-rule create \
29
37
  --body-json '{"name":"Sales read write","enabled":true,"type":"form","options":{"user_scope":[{"type":"user","id":2188889901}],"policy":{"key":"custom","actions":["view","add","edit"],"all_fields_read":false,"all_fields_write":false,"fields":[{"key":":1001","read":true,"write":true,"report":false},{"key":":1002","read":false,"write":false,"report":false},{"key":":1003","read":true,"write":true,"report":false}]},"list_options":{}}}'
30
38
  ```
31
39
 
40
+ ## Create Admin Full Access Rule
41
+
42
+ Use this for the operator's own admin entry before creating seed rows.
43
+
44
+ ```bash
45
+ arcubase-admin access-rule create \
46
+ --app-id 2188893436 \
47
+ --table-id 2188893443 \
48
+ --body-json '{"name":"Admin full access","enabled":true,"type":"form","options":{"user_scope":[{"type":"user","id":2188889977}],"policy":{"key":"custom","actions":["view","add","edit","delete"],"all_fields_read":true,"all_fields_write":true},"list_options":{}}}'
49
+ ```
50
+
32
51
  Rules:
33
52
 
34
53
  - read permission = `actions:["view"]`
@@ -23,7 +23,7 @@ Multi-line text field.
23
23
  "options": {
24
24
  "placeholder": "",
25
25
  "html": false,
26
- "size": "default",
26
+ "size": 6,
27
27
  "lengthLimit": false,
28
28
  "lengthMin": 0,
29
29
  "lengthMax": 2000
@@ -44,4 +44,4 @@ Multi-line text field.
44
44
 
45
45
  - `placeholder` matters only when `html=false`
46
46
  - when `lengthLimit=true`, it is recommended to provide both `lengthMin` and `lengthMax`
47
- - keep `size` within the values returned by the current editor; do not invent extra enum values
47
+ - `size` is numeric and maps to editor rows; use `6` for the default two-row textarea
@@ -1 +1 @@
1
- {"limit":20,"offset":0,"search":{"text_search":"lead"},"sorts":[{"column":":1001","order":"asc"}]}
1
+ {"limit":20,"offset":0,"search":{"q":"lead"},"sorts":[{"column":":1001","order":"asc"}]}
@@ -2,7 +2,7 @@
2
2
  "limit": 20,
3
3
  "offset": 0,
4
4
  "search": {
5
- "text_search": "SO-1001"
5
+ "q": "SO-1001"
6
6
  },
7
7
  "sorts": [
8
8
  { "column": ":1003", "order": "desc" }
@@ -1 +1 @@
1
- {"limit":20,"offset":0,"search":{"text_search":"SKU-1001"},"sorts":[{"column":":1001","order":"desc"}]}
1
+ {"limit":20,"offset":0,"search":{"q":"SKU-1001"},"sorts":[{"column":":1001","order":"desc"}]}
@@ -83,6 +83,12 @@ Typical row shape:
83
83
  }
84
84
  ```
85
85
 
86
+ Returned values:
87
+
88
+ - current row values are only in `fields`
89
+ - fields absent from `fields` are not returned to the current user and have no row-value evidence
90
+ - table schema `fields[].value` and `default_value_mode` describe schema defaults, not current row values
91
+
86
92
  ## Read one row
87
93
 
88
94
  Command:
@@ -96,6 +102,13 @@ arcubase row get \
96
102
 
97
103
  Use this when you already know the row id and want the full record payload.
98
104
 
105
+ Returned values:
106
+
107
+ - current row values are in `record.fields`
108
+ - fields absent from `record.fields` are not returned to the current user and have no row-value evidence
109
+ - use `fieldPermits` only to explain access, not as current row values
110
+ - read visibility status is `fieldPermits read:false`; `FIELD_PERMISSION_REQUIRED` is a write preflight error from row create/update
111
+
99
112
  ## Update a row
100
113
 
101
114
  Command:
@@ -139,6 +152,61 @@ Success result:
139
152
 
140
153
  The update command returns `true`, not the updated row body.
141
154
 
155
+ ## Import Records
156
+
157
+ Excel:
158
+
159
+ ```bash
160
+ arcubase row import-excel \
161
+ --app-id <app_id> \
162
+ --table-id <table_id> \
163
+ --file records.xlsx \
164
+ --mapping-file mapping.excel.json \
165
+ --ttl-seconds 300
166
+ ```
167
+
168
+ CSV:
169
+
170
+ ```bash
171
+ arcubase row import-csv \
172
+ --app-id <app_id> \
173
+ --table-id <table_id> \
174
+ --file records.csv \
175
+ --mapping-file mapping.csv.json \
176
+ --ttl-seconds 300
177
+ ```
178
+
179
+ Both commands accept local file paths only. CSV mapping uses headers. Excel mapping uses column letters. The mapping file is validated locally before the import task is started.
180
+
181
+ Excel `mapping.excel.json`:
182
+
183
+ ```json
184
+ {
185
+ "sheet": "Sheet1",
186
+ "mode": "addonly",
187
+ "headerRow": 1,
188
+ "dataStartRow": 2,
189
+ "fields": [
190
+ { "fieldId": 1001, "column": "A" }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ CSV `mapping.csv.json`:
196
+
197
+ ```json
198
+ {
199
+ "mode": "addonly",
200
+ "header": true,
201
+ "dataStartRow": 2,
202
+ "fields": [
203
+ { "fieldId": 1001, "header": "客户名称" }
204
+ ]
205
+ }
206
+ ```
207
+
208
+ For `updateonly` and `addorupdate`, include `recordId` in the mapping. Use `arcubase-admin table get --app-id <app_id> --table-id <table_id>` to inspect numeric field ids.
209
+
142
210
  ## Delete rows
143
211
 
144
212
  Command:
@@ -38,13 +38,38 @@ For extra query-string parameters, also use:
38
38
  "limit": 20,
39
39
  "offset": 0,
40
40
  "search": {
41
- "text_search": "ORD-3001"
41
+ "q": "ORD-3001"
42
42
  }
43
43
  }
44
44
  ```
45
45
 
46
46
  This is the current success path for free-text search.
47
47
 
48
+ Rules:
49
+
50
+ - `search` is route-query state, not `ConditionSet`
51
+ - every `search` value must be a string
52
+ - use `q` for free-text search
53
+ - use `tab` for a list tab id
54
+ - use `filter_:<field_id_or_prop>` for field filters
55
+ - do not use `search.conditions`, `search.condition`, `search.field_filters`, `filter`, `filters`, `where`, `order`, or `orderBy`
56
+
57
+ ## Field filter
58
+
59
+ `query.json`:
60
+
61
+ ```json
62
+ {
63
+ "limit": 20,
64
+ "offset": 0,
65
+ "search": {
66
+ "filter_:1002": "%7B%22is%22%3A%22member_1%22%7D"
67
+ }
68
+ }
69
+ ```
70
+
71
+ `filter_:...` values are URL-encoded `FilterCond` JSON strings. For example, `%7B%22is%22%3A%22member_1%22%7D` decodes to `{"is":"member_1"}`.
72
+
48
73
  ## Sorts
49
74
 
50
75
  `query.json`:
@@ -102,7 +102,7 @@ Minimal `table-create.json`:
102
102
  "depends": [],
103
103
  "key": "description",
104
104
  "code_index": false,
105
- "options": {"placeholder": "", "html": false, "size": "default", "lengthLimit": false, "lengthMin": 0, "lengthMax": 2000},
105
+ "options": {"placeholder": "", "html": false, "size": 6, "lengthLimit": false, "lengthMin": 0, "lengthMax": 2000},
106
106
  "transfers": null,
107
107
  "children": null
108
108
  },
@@ -68,6 +68,63 @@ export const adminCommands = [
68
68
  "queryParams": [],
69
69
  "responseType": "AppCreateByTenantsRespVO"
70
70
  },
71
+ {
72
+ "scope": "admin",
73
+ "module": "app",
74
+ "functionName": "createAppFromBundleURL",
75
+ "commandPath": [
76
+ "app",
77
+ "create-from-bundle-url"
78
+ ],
79
+ "method": "POST",
80
+ "endpoint": "/api/apps/import-task",
81
+ "pathParams": [],
82
+ "controller": "manual",
83
+ "requestType": null,
84
+ "queryParams": [
85
+ {
86
+ "key": "url",
87
+ "flag": "url",
88
+ "type": "string",
89
+ "hasDefault": false
90
+ },
91
+ {
92
+ "key": "name",
93
+ "flag": "name",
94
+ "type": "string",
95
+ "hasDefault": true
96
+ }
97
+ ],
98
+ "responseType": "AppBundleImportTaskCreateRespVO"
99
+ },
100
+ {
101
+ "scope": "admin",
102
+ "module": "app",
103
+ "functionName": "watchAppBundleImportTask",
104
+ "commandPath": [
105
+ "app",
106
+ "watch-import-task"
107
+ ],
108
+ "method": "GET",
109
+ "endpoint": "/api/apps/import-task/:task_id",
110
+ "pathParams": [
111
+ {
112
+ "param": "task_id",
113
+ "flag": "task-id"
114
+ }
115
+ ],
116
+ "controller": "manual",
117
+ "requestType": null,
118
+ "queryParams": [
119
+ {
120
+ "key": "ttl_seconds",
121
+ "flag": "ttl-seconds",
122
+ "type": "number",
123
+ "hasDefault": false
124
+ }
125
+ ],
126
+ "responseType": "AppBundleImportTaskStatusRespVO"
127
+ },
71
128
  {
72
129
  "scope": "admin",
73
130
  "module": "app",
@@ -791,6 +848,94 @@ export const userCommands = [
791
848
  "queryParams": [],
792
849
  "responseType": "EntityBulkUpdateRespVO"
793
850
  },
851
+ {
852
+ "scope": "user",
853
+ "module": "row",
854
+ "functionName": "importEntityRecordsFromExcel",
855
+ "commandPath": [
856
+ "row",
857
+ "import-excel"
858
+ ],
859
+ "method": "POST",
860
+ "endpoint": "/api/entity/:app_id/:entity_id/import",
861
+ "pathParams": [
862
+ {
863
+ "param": "app_id",
864
+ "flag": "app-id"
865
+ },
866
+ {
867
+ "param": "entity_id",
868
+ "flag": "table-id"
869
+ }
870
+ ],
871
+ "controller": "manual",
872
+ "requestType": null,
873
+ "queryParams": [
874
+ {
875
+ "key": "file",
876
+ "flag": "file",
877
+ "type": "string",
878
+ "hasDefault": false
879
+ },
880
+ {
881
+ "key": "mapping_file",
882
+ "flag": "mapping-file",
883
+ "type": "string",
884
+ "hasDefault": false
885
+ },
886
+ {
887
+ "key": "ttl_seconds",
888
+ "flag": "ttl-seconds",
889
+ "type": "number",
890
+ "hasDefault": true
891
+ }
892
+ ],
893
+ "responseType": "ImporterGetStateRespVO"
894
+ },
895
+ {
896
+ "scope": "user",
897
+ "module": "row",
898
+ "functionName": "importEntityRecordsFromCSV",
899
+ "commandPath": [
900
+ "row",
901
+ "import-csv"
902
+ ],
903
+ "method": "POST",
904
+ "endpoint": "/api/entity/:app_id/:entity_id/import",
905
+ "pathParams": [
906
+ {
907
+ "param": "app_id",
908
+ "flag": "app-id"
909
+ },
910
+ {
911
+ "param": "entity_id",
912
+ "flag": "table-id"
913
+ }
914
+ ],
915
+ "controller": "manual",
916
+ "requestType": null,
917
+ "queryParams": [
918
+ {
919
+ "key": "file",
920
+ "flag": "file",
921
+ "type": "string",
922
+ "hasDefault": false
923
+ },
924
+ {
925
+ "key": "mapping_file",
926
+ "flag": "mapping-file",
927
+ "type": "string",
928
+ "hasDefault": false
929
+ },
930
+ {
931
+ "key": "ttl_seconds",
932
+ "flag": "ttl-seconds",
933
+ "type": "number",
934
+ "hasDefault": true
935
+ }
936
+ ],
937
+ "responseType": "ImporterGetStateRespVO"
938
+ },
794
939
  {
795
940
  "scope": "user",
796
941
  "module": "row",
@@ -293,6 +293,7 @@ export interface KioskConfig {
293
293
  interactiveMode?: string;
294
294
  lookupFields?: number[];
295
295
  options?: Record<string, any>;
296
+ printers?: Record<string, any>[];
296
297
  screens?: KioskScreen[];
297
298
  theme?: string;
298
299
  }