@carthooks/arcubase-cli 0.1.10 → 0.1.13

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 (105) hide show
  1. package/bundle/arcubase-admin.mjs +1365 -375
  2. package/bundle/arcubase.mjs +1365 -375
  3. package/dist/generated/command_registry.generated.d.ts +36 -35
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +37 -36
  6. package/dist/generated/type_index.generated.d.ts +23 -23
  7. package/dist/generated/type_index.generated.js +23 -23
  8. package/dist/runtime/entity_save_schema.d.ts.map +1 -1
  9. package/dist/runtime/entity_save_schema.js +30 -0
  10. package/dist/runtime/env.d.ts +2 -1
  11. package/dist/runtime/env.d.ts.map +1 -1
  12. package/dist/runtime/env.js +10 -1
  13. package/dist/runtime/errors.d.ts +3 -0
  14. package/dist/runtime/errors.d.ts.map +1 -1
  15. package/dist/runtime/execute.d.ts +4 -2
  16. package/dist/runtime/execute.d.ts.map +1 -1
  17. package/dist/runtime/execute.js +1046 -134
  18. package/dist/runtime/paths.d.ts +1 -1
  19. package/dist/runtime/paths.d.ts.map +1 -1
  20. package/dist/runtime/paths.js +9 -2
  21. package/dist/runtime/upload.d.ts +1 -11
  22. package/dist/runtime/upload.d.ts.map +1 -1
  23. package/dist/runtime/upload.js +19 -133
  24. package/dist/runtime/zod_registry.d.ts +5 -3
  25. package/dist/runtime/zod_registry.d.ts.map +1 -1
  26. package/dist/runtime/zod_registry.js +108 -48
  27. package/dist/tests/bootstrap.test.js +12 -0
  28. package/dist/tests/command_registry.test.js +8 -0
  29. package/dist/tests/docs_readability.test.d.ts +2 -0
  30. package/dist/tests/docs_readability.test.d.ts.map +1 -0
  31. package/dist/tests/docs_readability.test.js +87 -0
  32. package/dist/tests/entity_save_schema.test.js +22 -0
  33. package/dist/tests/execute_validation.test.js +443 -44
  34. package/dist/tests/help.test.js +47 -8
  35. package/dist/tests/upload.test.js +30 -77
  36. package/package.json +1 -1
  37. package/sdk-dist/docs/runtime-reference/README.md +43 -29
  38. package/sdk-dist/docs/runtime-reference/access-rule.md +128 -0
  39. package/sdk-dist/docs/runtime-reference/condition.md +182 -0
  40. package/sdk-dist/docs/runtime-reference/entity-schema/README.md +6 -5
  41. package/sdk-dist/docs/runtime-reference/entity-schema/checkbox.md +1 -1
  42. package/sdk-dist/docs/runtime-reference/entity-schema/file.md +2 -2
  43. package/sdk-dist/docs/runtime-reference/entity-schema/image.md +2 -2
  44. package/sdk-dist/docs/runtime-reference/entity-schema/linkto.md +3 -3
  45. package/sdk-dist/docs/runtime-reference/entity-schema/number.md +1 -1
  46. package/sdk-dist/docs/runtime-reference/entity-schema/query.md +3 -3
  47. package/sdk-dist/docs/runtime-reference/entity-schema/relation.md +3 -3
  48. package/sdk-dist/docs/runtime-reference/entity-schema/relationfield.md +3 -3
  49. package/sdk-dist/docs/runtime-reference/entity-schema/status.md +1 -1
  50. package/sdk-dist/docs/runtime-reference/entity-schema/text.md +1 -1
  51. package/sdk-dist/docs/runtime-reference/entity-schema.md +28 -21
  52. package/sdk-dist/docs/runtime-reference/examples/README.md +1 -1
  53. package/sdk-dist/docs/runtime-reference/examples/crm-01/README.md +30 -11
  54. package/sdk-dist/docs/runtime-reference/examples/crm-01/contact.create-entity.json +1 -0
  55. package/sdk-dist/docs/runtime-reference/examples/crm-01/contact.schema.json +1 -0
  56. package/sdk-dist/docs/runtime-reference/examples/crm-01/create-app.json +1 -0
  57. package/sdk-dist/docs/runtime-reference/examples/crm-01/customer.create-entity.json +1 -0
  58. package/sdk-dist/docs/runtime-reference/examples/crm-01/customer.schema.json +1 -0
  59. package/sdk-dist/docs/runtime-reference/examples/crm-01/follow-up.create-entity.json +1 -0
  60. package/sdk-dist/docs/runtime-reference/examples/crm-01/follow-up.schema.json +1 -0
  61. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.bulk-update.json +1 -0
  62. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.create-entity.json +1 -0
  63. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.query.json +1 -0
  64. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.row.insert.json +1 -0
  65. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.schema.json +1 -0
  66. package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.selection.condition.json +1 -0
  67. package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +26 -26
  68. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.row.insert.json +1 -1
  69. package/sdk-dist/docs/runtime-reference/examples/wms-01/README.md +26 -10
  70. package/sdk-dist/docs/runtime-reference/examples/wms-01/create-app.json +1 -0
  71. package/sdk-dist/docs/runtime-reference/examples/wms-01/goods-receipt.create-entity.json +1 -0
  72. package/sdk-dist/docs/runtime-reference/examples/wms-01/goods-receipt.schema.json +1 -0
  73. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.bulk-update.json +1 -0
  74. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.create-entity.json +1 -0
  75. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.query.json +1 -0
  76. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.schema.json +1 -0
  77. package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.selection.condition.json +1 -0
  78. package/sdk-dist/docs/runtime-reference/examples/wms-01/item-master.create-entity.json +1 -0
  79. package/sdk-dist/docs/runtime-reference/examples/wms-01/item-master.schema.json +1 -0
  80. package/sdk-dist/docs/runtime-reference/examples/wms-01/stock-issue.create-entity.json +1 -0
  81. package/sdk-dist/docs/runtime-reference/examples/wms-01/stock-issue.schema.json +1 -0
  82. package/sdk-dist/docs/runtime-reference/row-crud.md +48 -8
  83. package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +31 -95
  84. package/sdk-dist/docs/runtime-reference/selection.md +128 -0
  85. package/sdk-dist/docs/runtime-reference/table-lifecycle.md +113 -30
  86. package/sdk-dist/docs/runtime-reference/uploads.md +30 -59
  87. package/sdk-dist/docs/runtime-reference/workflow/README.md +129 -13
  88. package/sdk-dist/generated/command_registry.generated.ts +37 -36
  89. package/sdk-dist/generated/type_index.generated.ts +23 -23
  90. package/src/generated/command_registry.generated.ts +37 -36
  91. package/src/generated/type_index.generated.ts +23 -23
  92. package/src/runtime/entity_save_schema.ts +30 -0
  93. package/src/runtime/env.ts +14 -2
  94. package/src/runtime/errors.ts +3 -0
  95. package/src/runtime/execute.ts +1272 -134
  96. package/src/runtime/paths.ts +11 -2
  97. package/src/runtime/upload.ts +19 -178
  98. package/src/runtime/zod_registry.ts +113 -48
  99. package/src/tests/bootstrap.test.ts +16 -0
  100. package/src/tests/command_registry.test.ts +9 -0
  101. package/src/tests/docs_readability.test.ts +96 -0
  102. package/src/tests/entity_save_schema.test.ts +22 -0
  103. package/src/tests/execute_validation.test.ts +577 -47
  104. package/src/tests/help.test.ts +51 -8
  105. package/src/tests/upload.test.ts +34 -91
@@ -1,9 +1,12 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { executeCLI } from '../runtime/execute.js';
4
+ import { listCommands } from '../runtime/command_registry.js';
5
+ import { getTypeHints } from '../runtime/zod_registry.js';
4
6
  const env = {
5
7
  ARCUBASE_BASE_URL: 'https://arcubase.example.com',
6
8
  ARCUBASE_ACCESS_TOKEN: 'tok',
9
+ ARCUBASE_SDK_ROOT: '/runtime/arcubase-sdk',
7
10
  ARCUBASE_TRACE_ID: '',
8
11
  ARCUBASE_SUBJECT_TYPE: '',
9
12
  ARCUBASE_SUBJECT_ID: '',
@@ -43,33 +46,69 @@ test('admin module help lists reset app commands', async () => {
43
46
  assert.doesNotMatch(out.text, /create-entity/);
44
47
  });
45
48
  test('user row help replaces workflow row commands', async () => {
46
- const out = await executeCLI('user', ['row', 'query', '--help'], undefined, async () => new Response('{}'));
49
+ const out = await executeCLI('user', ['row', 'query', '--help'], env, async () => new Response('{}'));
47
50
  assert.equal(out.kind, 'help');
48
- assert.match(out.text, /body: .*--body-file or --body-json/);
51
+ assert.match(out.text, /body: .*--body-json <json-string> \| --body-file <file>\.json/);
49
52
  assert.match(out.text, /query flags: --query-file/);
50
53
  assert.doesNotMatch(out.text, /workflow query-entity/);
51
54
  assert.doesNotMatch(out.text, /--entity-id/);
52
55
  });
56
+ test('user row help does not expose internal policy id input', async () => {
57
+ const out = await executeCLI('user', ['row', 'get', '--help'], env, async () => new Response('{}'));
58
+ assert.equal(out.kind, 'help');
59
+ assert.doesNotMatch(out.text, /--policy-id/);
60
+ assert.doesNotMatch(out.text, /policy_id/);
61
+ });
53
62
  test('admin table update-schema help uses table nouns and body-json', async () => {
54
- const out = await executeCLI('admin', ['table', 'update-schema', '--help'], undefined, async () => new Response('{}'));
63
+ const out = await executeCLI('admin', ['table', 'update-schema', '--help'], env, async () => new Response('{}'));
55
64
  assert.equal(out.kind, 'help');
56
- assert.match(out.text, /body: .*--body-file or --body-json/);
65
+ assert.match(out.text, /body: .*--body-json <json-string> \| --body-file <file>\.json/);
66
+ assert.match(out.text, /prefer --body-json/);
57
67
  assert.match(out.text, /path flags: --app-id, --table-id/);
58
68
  assert.doesNotMatch(out.text, /--entity-id/);
59
69
  assert.doesNotMatch(out.text, /admin-save-entity/);
60
70
  });
71
+ test('admin table create help gives one-call body-json example', async () => {
72
+ const out = await executeCLI('admin', ['table', 'create', '--help'], env, async () => new Response('{}'));
73
+ assert.equal(out.kind, 'help');
74
+ assert.match(out.text, /body-json example:/);
75
+ assert.match(out.text, /"field_id_seq":1003/);
76
+ assert.match(out.text, /"label":"投票描述"/);
77
+ assert.match(out.text, /"type":"textarea"/);
78
+ });
61
79
  test('admin access-rule help gives update whitelist and assign-users body-json examples', async () => {
62
- const update = await executeCLI('admin', ['access-rule', 'update', '--help'], undefined, async () => new Response('{}'));
80
+ const create = await executeCLI('admin', ['access-rule', 'create', '--help'], env, async () => new Response('{}'));
81
+ assert.equal(create.kind, 'help');
82
+ assert.match(create.text, /Access rule/);
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"\]/);
86
+ assert.match(create.text, /"user_scope":\[\{"type":"user","id":2188889901\}\]/);
87
+ assert.doesNotMatch(create.text, /"user_scope":\[\{"type":"member"/);
88
+ const update = await executeCLI('admin', ['access-rule', 'update', '--help'], env, async () => new Response('{}'));
63
89
  assert.equal(update.kind, 'help');
64
90
  assert.match(update.text, /--app-id/);
65
91
  assert.match(update.text, /--table-id/);
66
92
  assert.match(update.text, /--rule-id/);
67
93
  assert.match(update.text, /"update":\["enabled"\]/);
68
- const assign = await executeCLI('admin', ['access-rule', 'assign-users', '--help'], undefined, async () => new Response('{}'));
94
+ assert.match(update.text, /"actions":\["view","add","edit"\]/);
95
+ const assign = await executeCLI('admin', ['access-rule', 'assign-users', '--help'], env, async () => new Response('{}'));
69
96
  assert.equal(assign.kind, 'help');
70
97
  assert.match(assign.text, /"update":\["user_scope"\]/);
71
- assert.match(assign.text, /\{"update":\["user_scope"\],"options":\{"user_scope":\[\{"type":"member","id":123\}\]\}\}/);
72
- assert.doesNotMatch(assign.text, /"type":"user"/);
98
+ assert.match(assign.text, /\{"update":\["user_scope"\],"options":\{"user_scope":\[\{"type":"user","id":2188889901\}\]\}\}/);
99
+ assert.doesNotMatch(assign.text, /"type":"member"/);
73
100
  assert.doesNotMatch(assign.text, /"users"/);
74
101
  assert.doesNotMatch(assign.text, /"allowedUsers"/);
75
102
  });
103
+ test('every body command help exposes readable TypeScript type paths', async () => {
104
+ for (const scope of ['admin', 'user']) {
105
+ for (const command of listCommands(scope).filter((item) => item.requestType)) {
106
+ const out = await executeCLI(scope, [...command.commandPath, '--help'], env, async () => new Response('{}'));
107
+ assert.equal(out.kind, 'help');
108
+ assert.match(out.text, /^types:$/m, `${scope} ${command.commandPath.join(' ')} must expose types section`);
109
+ for (const hint of getTypeHints(command.requestType ?? '', env)) {
110
+ assert.match(out.text, new RegExp(`${hint.symbol}: ${hint.file}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
111
+ }
112
+ }
113
+ }
114
+ });
@@ -1,106 +1,59 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import fs from 'fs';
4
- import os from 'os';
5
- import path from 'path';
6
3
  import { executeCLI } from '../runtime/execute.js';
4
+ import { CLIError } from '../runtime/errors.js';
7
5
  const env = {
8
6
  ARCUBASE_BASE_URL: 'https://arcubase.example.com',
9
7
  ARCUBASE_ACCESS_TOKEN: 'tok',
8
+ ARCUBASE_SDK_ROOT: '/runtime/arcubase-sdk',
10
9
  ARCUBASE_TRACE_ID: '',
11
10
  ARCUBASE_SUBJECT_TYPE: '',
12
11
  ARCUBASE_SUBJECT_ID: '',
13
12
  ARCUBASE_TOKEN_EXPIRES_AT: '',
14
13
  };
15
- test('upload help works without runtime env', async () => {
16
- const out = await executeCLI('user', ['upload', '--help'], undefined, async () => new Response('{}'));
14
+ test('upload module help exposes reset upload commands with injected SDK root', async () => {
15
+ const out = await executeCLI('user', ['upload', '--help'], env, async () => new Response('{}'));
17
16
  assert.equal(out.kind, 'help');
18
- assert.match(out.text, /arcubase upload <local-file>/);
19
- assert.match(out.text, /uploads\.md/);
17
+ assert.match(out.text, /commands:/);
18
+ assert.match(out.text, /token/);
19
+ assert.match(out.text, /preview/);
20
+ assert.match(out.text, /mobile/);
21
+ assert.match(out.text, /check-mobile/);
22
+ assert.doesNotMatch(out.text, /<local-file>/);
20
23
  });
21
- test('upload returns file field value array for row payloads', async () => {
22
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'arcubase-upload-'));
23
- const sourceFile = path.join(tempDir, 'contract.pdf');
24
- fs.writeFileSync(sourceFile, 'fake pdf bytes');
24
+ test('upload rejects local file shortcut', async () => {
25
+ await assert.rejects(async () => {
26
+ await executeCLI('user', ['upload', './contract.pdf'], env, async () => new Response('{}'));
27
+ }, (error) => {
28
+ assert.ok(error instanceof CLIError);
29
+ assert.equal(error.code, 'UNKNOWN_COMMAND');
30
+ assert.match(error.message, /unknown command: upload \.\/contract\.pdf/);
31
+ return true;
32
+ });
33
+ });
34
+ test('upload token executes the explicit API command', async () => {
25
35
  const calls = [];
26
- const result = await executeCLI('user', ['upload', sourceFile], env, async (input, init) => {
36
+ const result = await executeCLI('user', ['upload', 'token', '--body-json', '{"global":false}'], env, async (input, init) => {
27
37
  const url = String(input);
28
- calls.push({ url, method: init?.method });
38
+ calls.push({ url, method: init?.method, body: typeof init?.body === 'string' ? init.body : undefined });
29
39
  if (url === 'https://arcubase.example.com/api/upload/token') {
30
40
  return new Response(JSON.stringify({
31
41
  data: {
32
42
  id: 123456,
33
43
  mode: 'aliyun-oss',
34
- token_data: {
35
- dir: 'upload/123456/',
36
- policy: 'policy',
37
- accessid: 'accessid',
38
- signature: 'signature',
39
- host: 'https://uploads.example.com',
40
- },
44
+ token_data: { host: 'https://uploads.example.com' },
41
45
  },
42
46
  }), { status: 200, headers: { 'Content-Type': 'application/json' } });
43
47
  }
44
- if (url === 'https://uploads.example.com') {
45
- return new Response('', { status: 200 });
46
- }
47
48
  throw new Error(`unexpected fetch url: ${url}`);
48
49
  });
49
50
  assert.equal(result.kind, 'result');
50
- assert.deepEqual(calls, [
51
- { url: 'https://arcubase.example.com/api/upload/token', method: 'POST' },
52
- { url: 'https://uploads.example.com', method: 'POST' },
53
- ]);
54
- assert.deepEqual(result.data, [
55
- {
56
- upload_id: 123456,
57
- file: 'contract.pdf',
58
- file_name: 'contract.pdf',
59
- meta: {},
51
+ assert.deepEqual(calls, [{ url: 'https://arcubase.example.com/api/upload/token', method: 'POST', body: '{"global":false}' }]);
52
+ assert.deepEqual(result.data, {
53
+ data: {
54
+ id: 123456,
55
+ mode: 'aliyun-oss',
56
+ token_data: { host: 'https://uploads.example.com' },
60
57
  },
61
- ]);
62
- });
63
- test('upload supports s3-post-policy tokens', async () => {
64
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'arcubase-upload-s3-'));
65
- const sourceFile = path.join(tempDir, 'contract.pdf');
66
- fs.writeFileSync(sourceFile, 'fake pdf bytes');
67
- const calls = [];
68
- const result = await executeCLI('user', ['upload', sourceFile], env, async (input, init) => {
69
- const url = String(input);
70
- calls.push({ url, method: init?.method });
71
- if (url === 'https://arcubase.example.com/api/upload/token') {
72
- return new Response(JSON.stringify({
73
- data: {
74
- id: 654321,
75
- mode: 's3-post-policy',
76
- token_data: {
77
- dir: 'upload/654321/',
78
- policy: 'policy',
79
- host: 'https://uploads.example.com/private-bucket',
80
- x_amz_algorithm: 'AWS4-HMAC-SHA256',
81
- x_amz_credential: 'test/20260504/us-east-1/s3/aws4_request',
82
- x_amz_date: '20260504T010203Z',
83
- x_amz_signature: 'abc123',
84
- },
85
- },
86
- }), { status: 200, headers: { 'Content-Type': 'application/json' } });
87
- }
88
- if (url === 'https://uploads.example.com/private-bucket') {
89
- return new Response('', { status: 200 });
90
- }
91
- throw new Error(`unexpected fetch url: ${url}`);
92
58
  });
93
- assert.equal(result.kind, 'result');
94
- assert.deepEqual(calls, [
95
- { url: 'https://arcubase.example.com/api/upload/token', method: 'POST' },
96
- { url: 'https://uploads.example.com/private-bucket', method: 'POST' },
97
- ]);
98
- assert.deepEqual(result.data, [
99
- {
100
- upload_id: 654321,
101
- file: 'contract.pdf',
102
- file_name: 'contract.pdf',
103
- meta: {},
104
- },
105
- ]);
106
59
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carthooks/arcubase-cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.13",
4
4
  "description": "Arcubase runtime CLI for admin and user command execution",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,13 +11,14 @@ Read it as a command cheatsheet:
11
11
  - what result shape to expect
12
12
 
13
13
  Do not start from raw HTTP methods or paths. Start from the CLI command.
14
+ Prefer `--body-json <json-string>` for normal payloads, including table schema, to avoid an extra agent file operation. Use `--body-file <file>.json` only when the JSON is too large for one tool call.
14
15
 
15
16
  ## Paths
16
17
 
17
18
  - Development source:
18
19
  - `misc/packages/@carthooks/arcubase-cli/docs/runtime-reference/`
19
20
  - Runtime location:
20
- - `/opt/arcubase-sdk/docs/runtime-reference/`
21
+ - `$ARCUBASE_SDK_ROOT/docs/runtime-reference/`
21
22
  - Published path:
22
23
  - `sdk-dist/docs/runtime-reference/`
23
24
 
@@ -26,18 +27,18 @@ Do not start from raw HTTP methods or paths. Start from the CLI command.
26
27
  Use `arcubase-admin` for:
27
28
 
28
29
  - app creation
29
- - entity shell creation
30
- - entity schema save
31
- - entity tag admin actions
30
+ - table shell creation
31
+ - table schema save
32
+ - access-rule and workflow setup
32
33
 
33
34
  Common commands:
34
35
 
35
36
  ```bash
36
- arcubase-admin app create-app-by-tenants --body-file create-app.json
37
- arcubase-admin app create-entity --id <app_id> --body-file create-entity.json
38
- arcubase-admin entity admin-get-entity-info --app-id <app_id> --table-id <table_id>
39
- arcubase-admin entity admin-save-entity --app-id <app_id> --table-id <table_id> --body-file save-entity.json
40
- arcubase-admin entity-tags get-selection-tags --appId <app_id> --entityId <table_id> --body-file get-tags.json
37
+ arcubase-admin app create --body-file create-app.json | --body-json <json-string>
38
+ arcubase-admin table create --app-id <app_id> --body-json <json-string> | --body-file table-create.json
39
+ arcubase-admin table get --app-id <app_id> --table-id <table_id>
40
+ arcubase-admin table update-schema --app-id <app_id> --table-id <table_id> --body-json <json-string> | --body-file table-schema.json
41
+ 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
41
42
  ```
42
43
 
43
44
  Use `arcubase` for:
@@ -49,31 +50,31 @@ Use `arcubase` for:
49
50
  - search
50
51
  - selection actions
51
52
  - bulk update
52
- - batch operator
53
53
 
54
54
  Common commands:
55
55
 
56
56
  ```bash
57
- arcubase row create --app-id <app_id> --table-id <table_id> --body-file insert.json
58
- arcubase row query --app-id <app_id> --table-id <table_id> --body-file query.json
59
- arcubase row update --app-id <app_id> --table-id <table_id> --row-id <row_id> --body-file update.json
60
- arcubase row delete --app-id <app_id> --table-id <table_id> --body-file delete.json
61
- arcubase row selection-action --app-id <app_id> --table-id <table_id> --action archive --body-file selection.json
62
- arcubase row bulk-update --app-id <app_id> --table-id <table_id> --body-file bulk-update.json
57
+ arcubase row create --app-id <app_id> --table-id <table_id> --body-file insert.json | --body-json <json-string>
58
+ arcubase row query --app-id <app_id> --table-id <table_id> --body-file query.json | --body-json <json-string>
59
+ arcubase row update --app-id <app_id> --table-id <table_id> --row-id <row_id> --body-file update.json | --body-json <json-string>
60
+ arcubase row delete --app-id <app_id> --table-id <table_id> --body-file delete.json | --body-json <json-string>
61
+ arcubase row update --app-id <app_id> --table-id <table_id> --body-file restore.json | --body-json <json-string>
62
+ arcubase row selection-action --app-id <app_id> --table-id <table_id> --action archive --body-file selection.json | --body-json <json-string>
63
+ arcubase row bulk-update --app-id <app_id> --table-id <table_id> --body-file bulk-update.json | --body-json <json-string>
63
64
  ```
64
65
 
65
66
  Hard rule:
66
67
 
68
+ - `table create` must include non-empty `fields`; empty table shell creation is not allowed
67
69
  - `arcubase-admin` stops at schema save
68
70
  - all row operations must switch to `arcubase`
69
- - do not use `arcubase-admin row create`
70
71
  - do not use `arcubase-admin row query`
71
72
 
72
73
  ## Required file inputs
73
74
 
74
75
  Most write commands use:
75
76
 
76
- - `--body-file <file>.json`
77
+ - `--body-file <file>.json` or `--body-json <json-string>`
77
78
 
78
79
  Some query commands also use:
79
80
 
@@ -88,7 +89,7 @@ arcubase row query \
88
89
  --app-id <app_id> \
89
90
  --table-id <table_id> \
90
91
  --query-file archived-query.json \
91
- --body-file archived-body.json
92
+ --body-file archived-body.json | --body-json <json-string>
92
93
  ```
93
94
 
94
95
  ## Runtime env
@@ -97,6 +98,8 @@ The CLIs accept either generic env keys:
97
98
 
98
99
  - `ARCUBASE_BASE_URL`
99
100
  - `ARCUBASE_ACCESS_TOKEN`
101
+ - `ARCUBASE_SDK_ROOT`
102
+ - optional: `ARCUBASE_TRACE_ID`
100
103
 
101
104
  or scoped env keys:
102
105
 
@@ -107,6 +110,9 @@ or scoped env keys:
107
110
  - `ARCUBASE_USER_BASE_URL`
108
111
  - `ARCUBASE_USER_ACCESS_TOKEN`
109
112
 
113
+ Tenant identity comes from the JWT access token. Do not pass tenant ids through CLI env.
114
+ - `ARCUBASE_USER_HOST`
115
+
110
116
  Help should work without runtime env:
111
117
 
112
118
  ```bash
@@ -118,7 +124,7 @@ arcubase <module> --help
118
124
  arcubase <module> <command> --help
119
125
  ```
120
126
 
121
- Command help is also the discovery entrypoint for this doc set.
127
+ Command help is also the index entrypoint for this doc set.
122
128
 
123
129
  When a command is tied to a common task, `--help` should print:
124
130
 
@@ -149,10 +155,17 @@ If you need a business-shaped template:
149
155
  2. pick the closest domain directory
150
156
  3. copy and adapt the example schema and payload files
151
157
 
152
- If you need search, selection, tags, bulk update, or batch operator:
158
+ If you need search, selection, or bulk update:
153
159
 
154
160
  1. read `search-and-bulk-actions.md`
155
- 2. use `row-crud.md` only for row result shapes
161
+ 2. read `selection.md`
162
+ 3. read `condition.md`
163
+ 4. use `row-crud.md` only for row result shapes
164
+
165
+ If you need workflow actions:
166
+
167
+ 1. read `workflow/README.md`
168
+ 2. use the exact `task_id`, node, record, and user ids returned by Arcubase
156
169
 
157
170
  ## Covered now
158
171
 
@@ -167,13 +180,14 @@ If you need search, selection, tags, bulk update, or batch operator:
167
180
  - `row-crud.md`
168
181
  - insert, query, update, delete, restore
169
182
  - `uploads.md`
170
- - upload one local file and reuse the returned field value in row payloads
183
+ - upload token, preview, mobile upload, and mobile upload check API payloads
171
184
  - `search-and-bulk-actions.md`
172
- - search, selection, tags, bulk update, batch operator
185
+ - search, selection, bulk update
186
+ - `selection.md`
187
+ - `EntitySelectionVO`, ids/all/condition selection, command action matrix
188
+ - `condition.md`
189
+ - selection condition, `FilterCond`, and `ConditionSet` differences
190
+ - `workflow/README.md`
191
+ - process map, approve, rollback, countersign, forward, retract, resolve, approver query, node query
173
192
  - `examples/`
174
193
  - business-shaped schema and payload templates
175
-
176
- ## Reserved expansion
177
-
178
- - `workflow/README.md`
179
- - reserved for future workflow-specific CLI docs
@@ -0,0 +1,128 @@
1
+ # Access Rule
2
+
3
+ Use `arcubase-admin access-rule` for app/table access.
4
+
5
+ ## One Path
6
+
7
+ 1. Get the numeric `TenantUser.ID`.
8
+ 2. Create an enabled rule with `options.policy`.
9
+ 3. Assign users with `options.user_scope`.
10
+ 4. Verify with `access-rule get`.
11
+
12
+ ## User Mapping
13
+
14
+ ```bash
15
+ arcubase-admin get-digiemployee-users
16
+ ```
17
+
18
+ Use the numeric `tenantUserId` / `TenantUser.ID`.
19
+
20
+ Do not use hash ids such as `arcubaseTenantUserId`.
21
+ Use `user_scope[].type:"user"` for a tenant user. Do not use FieldVO field type `member` in access rules.
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
+
31
+ ## Create Read/Write Rule With Hidden Field
32
+
33
+ ```bash
34
+ arcubase-admin access-rule create \
35
+ --app-id 2188893436 \
36
+ --table-id 2188893443 \
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":{}}}'
38
+ ```
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
+
51
+ Rules:
52
+
53
+ - read permission = `actions:["view"]`
54
+ - write permission = add `add` and `edit`
55
+ - field key is `":<field_id>"`, not a number
56
+ - field key is not the FieldVO `key`; do not use values like `vote_description`
57
+ - hidden field = `{"key":":1002","read":false,"write":false,"report":false}`
58
+ - when `all_fields_read=false` or `all_fields_write=false`, list each visible field with `read/write=true`
59
+ - do not use `permissions`
60
+ - do not use `field_visibility`
61
+ - do not use `actions:["read","insert"]`
62
+
63
+ ## Enable Rule
64
+
65
+ ```bash
66
+ arcubase-admin access-rule update \
67
+ --app-id 2188893436 \
68
+ --table-id 2188893443 \
69
+ --rule-id 2188893557 \
70
+ --body-json '{"update":["enabled"],"enabled":true}'
71
+ ```
72
+
73
+ ## Update Policy With Hidden Field
74
+
75
+ Use this when the rule already exists and the field visibility or actions must change.
76
+
77
+ ```bash
78
+ arcubase-admin access-rule update \
79
+ --app-id 2188893436 \
80
+ --table-id 2188893443 \
81
+ --rule-id 2188893557 \
82
+ --body-json '{"update":["options"],"options":{"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":{}}}'
83
+ ```
84
+
85
+ Rules:
86
+
87
+ - use `update:["options"]`
88
+ - do not use `update:["options.policy"]`
89
+ - field key is `":<field_id>"`, not a number
90
+ - field key is not the FieldVO `key`; do not use values like `vote_description`
91
+ - preserve `list_options:{}` unless the existing rule has a known replacement
92
+
93
+ ## Assign Users Only
94
+
95
+ Use this only when the rule already has the correct `policy`.
96
+
97
+ ```bash
98
+ arcubase-admin access-rule assign-users \
99
+ --app-id 2188893436 \
100
+ --table-id 2188893443 \
101
+ --rule-id 2188893557 \
102
+ --body-json '{"update":["user_scope"],"options":{"user_scope":[{"type":"user","id":2188889901}]}}'
103
+ ```
104
+
105
+ Rules:
106
+
107
+ - `assign-users` preserves the existing policy.
108
+ - body must use `update:["user_scope"]`.
109
+ - body must use `options.user_scope`.
110
+ - `user_scope[].type` is `user` for tenant users.
111
+ - `user_scope[].id` is numeric `TenantUser.ID`.
112
+
113
+ ## Verify
114
+
115
+ ```bash
116
+ arcubase-admin access-rule get \
117
+ --app-id 2188893436 \
118
+ --table-id 2188893443 \
119
+ --rule-id 2188893557
120
+ ```
121
+
122
+ Success:
123
+
124
+ - `enabled=true`
125
+ - `options.user_scope` contains the numeric user id
126
+ - `options.policy.actions` contains `view`, `add`, `edit`
127
+ - `options.policy.fields` contains hidden fields as `{"key":":1002","read":false,"write":false,"report":false}`
128
+ - visible fields are explicitly present with `read/write=true` when `all_fields_read/write=false`