@carthooks/arcubase-cli 0.1.4 → 0.1.6

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 (49) hide show
  1. package/bundle/arcubase-admin.mjs +188 -210
  2. package/bundle/arcubase.mjs +188 -210
  3. package/dist/generated/command_registry.generated.d.ts +36 -112
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +40 -163
  6. package/dist/generated/type_index.generated.d.ts +0 -12
  7. package/dist/generated/type_index.generated.d.ts.map +1 -1
  8. package/dist/generated/type_index.generated.js +0 -12
  9. package/dist/generated/zod_registry.generated.d.ts +1 -19
  10. package/dist/generated/zod_registry.generated.d.ts.map +1 -1
  11. package/dist/generated/zod_registry.generated.js +0 -21
  12. package/dist/runtime/command_registry.d.ts +19 -1
  13. package/dist/runtime/command_registry.d.ts.map +1 -1
  14. package/dist/runtime/command_registry.js +19 -1
  15. package/dist/runtime/execute.d.ts.map +1 -1
  16. package/dist/runtime/execute.js +119 -9
  17. package/dist/runtime/zod_registry.d.ts.map +1 -1
  18. package/dist/runtime/zod_registry.js +24 -7
  19. package/dist/tests/command_registry.test.js +11 -4
  20. package/dist/tests/execute_validation.test.js +72 -17
  21. package/dist/tests/help.test.js +16 -9
  22. package/package.json +1 -1
  23. package/sdk-dist/api/admin/app.ts +8 -1
  24. package/sdk-dist/docs/runtime-reference/README.md +24 -16
  25. package/sdk-dist/docs/runtime-reference/app-discovery.md +84 -0
  26. package/sdk-dist/docs/runtime-reference/entity-schema/README.md +2 -2
  27. package/sdk-dist/docs/runtime-reference/entity-schema/subform.md +2 -2
  28. package/sdk-dist/docs/runtime-reference/entity-schema.md +2 -2
  29. package/sdk-dist/docs/runtime-reference/examples/README.md +1 -1
  30. package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +4 -4
  31. package/sdk-dist/docs/runtime-reference/examples/oms-01/app-overview.md +1 -1
  32. package/sdk-dist/docs/runtime-reference/row-crud.md +6 -6
  33. package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +5 -5
  34. package/sdk-dist/docs/runtime-reference/table-lifecycle.md +24 -8
  35. package/sdk-dist/docs/runtime-reference/uploads.md +1 -1
  36. package/sdk-dist/generated/command_registry.generated.ts +40 -163
  37. package/sdk-dist/generated/type_index.generated.ts +0 -12
  38. package/sdk-dist/generated/zod_registry.generated.ts +0 -36
  39. package/sdk-dist/types/app.ts +7 -0
  40. package/src/generated/command_registry.generated.ts +40 -163
  41. package/src/generated/type_index.generated.ts +0 -12
  42. package/src/generated/zod_registry.generated.ts +0 -36
  43. package/src/runtime/command_registry.ts +38 -2
  44. package/src/runtime/execute.ts +127 -9
  45. package/src/runtime/zod_registry.ts +25 -7
  46. package/src/tests/command_registry.test.ts +13 -4
  47. package/src/tests/execute_validation.test.ts +78 -17
  48. package/src/tests/help.test.ts +17 -9
  49. package/sdk-dist/docs/runtime-reference/workflow/README.md +0 -19
@@ -67,12 +67,19 @@ const examplesIndexDocHint: RuntimeDocHint = {
67
67
  file: '/opt/arcubase-sdk/docs/runtime-reference/examples/README.md',
68
68
  }
69
69
 
70
+ const appDiscoveryDocHint: RuntimeDocHint = {
71
+ title: 'App discovery',
72
+ file: '/opt/arcubase-sdk/docs/runtime-reference/app-discovery.md',
73
+ }
74
+
70
75
  const docHintIndex: Record<string, RuntimeDocHint[]> = {
71
76
  AppCreateByTenantsReqVO: [
77
+ appDiscoveryDocHint,
72
78
  { title: 'Table lifecycle', file: '/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md' },
73
79
  examplesIndexDocHint,
74
80
  ],
75
81
  AppCreateEntityReqVO: [
82
+ appDiscoveryDocHint,
76
83
  { title: 'Table lifecycle', file: '/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md' },
77
84
  examplesIndexDocHint,
78
85
  ],
@@ -148,14 +155,25 @@ export function getDocHints(typeName: string): RuntimeDocHint[] {
148
155
 
149
156
  const commandDocHintIndex: Record<string, RuntimeDocHint[]> = {
150
157
  'admin.app.createAppByTenants': [
158
+ appDiscoveryDocHint,
151
159
  { title: 'Table lifecycle', file: '/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md' },
152
160
  examplesIndexDocHint,
153
161
  ],
162
+ 'admin.app.listApps': [
163
+ appDiscoveryDocHint,
164
+ examplesIndexDocHint,
165
+ ],
166
+ 'admin.app.inventory': [
167
+ appDiscoveryDocHint,
168
+ examplesIndexDocHint,
169
+ ],
154
170
  'admin.app.createEntity': [
171
+ appDiscoveryDocHint,
155
172
  { title: 'Table lifecycle', file: '/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md' },
156
173
  examplesIndexDocHint,
157
174
  ],
158
175
  'admin.entity.adminGetEntityInfo': [
176
+ appDiscoveryDocHint,
159
177
  { title: 'Table lifecycle', file: '/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md' },
160
178
  examplesIndexDocHint,
161
179
  ],
@@ -165,34 +183,34 @@ const commandDocHintIndex: Record<string, RuntimeDocHint[]> = {
165
183
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
166
184
  examplesIndexDocHint,
167
185
  ],
168
- 'user.workflow.insertEntity': [
186
+ 'user.rows.insertEntity': [
169
187
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
170
188
  { title: 'Uploads', file: '/opt/arcubase-sdk/docs/runtime-reference/uploads.md' },
171
189
  examplesIndexDocHint,
172
190
  ],
173
- 'user.workflow.queryEntity': [
191
+ 'user.rows.queryEntity': [
174
192
  { title: 'Search and bulk actions', file: '/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md' },
175
193
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
176
194
  examplesIndexDocHint,
177
195
  ],
178
- 'user.workflow.getEntityRow': [
196
+ 'user.rows.getEntityRow': [
179
197
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
180
198
  examplesIndexDocHint,
181
199
  ],
182
- 'user.workflow.updateEntityRow': [
200
+ 'user.rows.updateEntityRow': [
183
201
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
184
202
  { title: 'Uploads', file: '/opt/arcubase-sdk/docs/runtime-reference/uploads.md' },
185
203
  examplesIndexDocHint,
186
204
  ],
187
- 'user.workflow.queryEntitySelection': [
205
+ 'user.rows.queryEntitySelection': [
188
206
  { title: 'Search and bulk actions', file: '/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md' },
189
207
  examplesIndexDocHint,
190
208
  ],
191
- 'user.workflow.deleteEntityRow': [
209
+ 'user.rows.deleteEntityRow': [
192
210
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
193
211
  examplesIndexDocHint,
194
212
  ],
195
- 'user.workflow.restoreEntityRow': [
213
+ 'user.rows.restoreEntityRow': [
196
214
  { title: 'Row CRUD', file: '/opt/arcubase-sdk/docs/runtime-reference/row-crud.md' },
197
215
  examplesIndexDocHint,
198
216
  ],
@@ -8,8 +8,17 @@ test('admin app create-entity command exists', () => {
8
8
  assert.ok(adminCommands.some((c) => c.commandPath.join(' ') === 'app create-entity'))
9
9
  })
10
10
 
11
- test('user workflow query-entity command exists', () => {
12
- assert.ok(userCommands.some((c) => c.commandPath.join(' ') === 'workflow query-entity'))
11
+ test('admin app list-apps command exists', () => {
12
+ assert.ok(adminCommands.some((c) => c.commandPath.join(' ') === 'app list-apps'))
13
+ })
14
+
15
+ test('admin app inventory command exists', () => {
16
+ const suggestions = findCommandSuggestions('app', 'inventory', 20)
17
+ assert.ok(suggestions.some((item) => item.binary === 'arcubase-admin' && item.moduleName === 'app' && item.commandName === 'inventory'))
18
+ })
19
+
20
+ test('user rows query-entity command exists', () => {
21
+ assert.ok(userCommands.some((c) => c.commandPath.join(' ') === 'rows query-entity'))
13
22
  })
14
23
 
15
24
  test('inline request type keeps full object text instead of truncated brace', () => {
@@ -27,7 +36,7 @@ test('tag commands no longer emit truncated inline request type', () => {
27
36
  assert.equal(typed.scalarParams.length, 0)
28
37
  })
29
38
 
30
- test('suggestions can point wrong admin delete guess to user workflow delete command', () => {
39
+ test('suggestions can point wrong admin delete guess to user rows delete command', () => {
31
40
  const suggestions = findCommandSuggestions('entity', 'admin-delete-entity-row')
32
- assert.ok(suggestions.some((item) => item.binary === 'arcubase' && item.moduleName === 'workflow' && item.commandName === 'delete-entity-row'))
41
+ assert.ok(suggestions.some((item) => item.binary === 'arcubase' && item.moduleName === 'rows' && item.commandName === 'delete-entity-row'))
33
42
  })
@@ -82,6 +82,67 @@ test('admin api-prefixed command executes against external root path', async ()
82
82
  assert.equal(out.data.url, 'https://arcubase.example.com/apps/app_1/entity-shares')
83
83
  })
84
84
 
85
+ test('app inventory returns apps with entity lists', async () => {
86
+ const calls: string[] = []
87
+ const out = await executeCLI(
88
+ 'admin',
89
+ ['app', 'inventory'],
90
+ env as any,
91
+ async (url, init) => {
92
+ calls.push(`${init?.method ?? 'GET'} ${String(url)}`)
93
+ if (String(url) === 'https://arcubase.example.com/apps') {
94
+ return new Response(JSON.stringify({
95
+ data: [
96
+ { id: 101, name: 'Workspace' },
97
+ { id: 102, name: 'CRM' },
98
+ ],
99
+ }), { status: 200 })
100
+ }
101
+ if (String(url) === 'https://arcubase.example.com/apps/101/entities') {
102
+ return new Response(JSON.stringify({
103
+ data: [
104
+ { id: 201, name: 'Orders' },
105
+ { id: 202, name: 'Customers' },
106
+ ],
107
+ }), { status: 200 })
108
+ }
109
+ if (String(url) === 'https://arcubase.example.com/apps/102/entities') {
110
+ return new Response(JSON.stringify({
111
+ data: [
112
+ { id: 203, name: 'Leads' },
113
+ ],
114
+ }), { status: 200 })
115
+ }
116
+ return new Response('{}', { status: 404 })
117
+ },
118
+ )
119
+ assert.equal(out.kind, 'result')
120
+ assert.deepEqual(out.data, {
121
+ apps: [
122
+ {
123
+ id: '101',
124
+ name: 'Workspace',
125
+ entities: [
126
+ { id: '201', name: 'Orders' },
127
+ { id: '202', name: 'Customers' },
128
+ ],
129
+ },
130
+ {
131
+ id: '102',
132
+ name: 'CRM',
133
+ entities: [
134
+ { id: '203', name: 'Leads' },
135
+ ],
136
+ },
137
+ ],
138
+ })
139
+ assert.deepEqual(calls, [
140
+ 'GET https://arcubase.example.com/apps',
141
+ 'POST https://arcubase.example.com/apps/101/entities',
142
+ 'POST https://arcubase.example.com/apps/102/entities',
143
+ ])
144
+ })
145
+
85
146
  test('admin save entity treats top-level error body as failure even on http 200', async () => {
86
147
  const bodyFile = tempJSONFile({
87
148
  id: 2188889845,
@@ -224,7 +285,7 @@ test('delete entity row rejects selection type outside ids', async () => {
224
285
  await assert.rejects(async () => {
225
286
  await executeCLI(
226
287
  'user',
227
- ['workflow', 'delete-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
288
+ ['rows', 'delete-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
228
289
  env as any,
229
290
  async () => new Response('{}', { status: 200 }),
230
291
  )
@@ -283,7 +344,7 @@ test('selection action accepts condition selection with selectAll', async () =>
283
344
 
284
345
  const out = await executeCLI(
285
346
  'user',
286
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
347
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
287
348
  env as any,
288
349
  async (_url, init) => new Response(String(init?.body ?? '{}'), { status: 200 }),
289
350
  )
@@ -306,7 +367,7 @@ test('selection action accepts condition selection with filter_* keys', async ()
306
367
 
307
368
  const out = await executeCLI(
308
369
  'user',
309
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
370
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
310
371
  env as any,
311
372
  async (_url, init) => new Response(String(init?.body ?? '{}'), { status: 200 }),
312
373
  )
@@ -328,7 +389,7 @@ test('selection action accepts condition selection with selectAll only for batch
328
389
 
329
390
  const out = await executeCLI(
330
391
  'user',
331
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'batch_print', '--body-file', bodyFile],
392
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'batch_print', '--body-file', bodyFile],
332
393
  env as any,
333
394
  async (_url, init) => new Response(String(init?.body ?? '{}'), { status: 200 }),
334
395
  )
@@ -346,7 +407,7 @@ test('selection action accepts all selection for query-style actions', async ()
346
407
 
347
408
  const out = await executeCLI(
348
409
  'user',
349
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'query', '--body-file', bodyFile],
410
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'query', '--body-file', bodyFile],
350
411
  env as any,
351
412
  async (_url, init) => new Response(String(init?.body ?? '{}'), { status: 200 }),
352
413
  )
@@ -369,7 +430,7 @@ test('selection action rejects condition selection for query action', async () =
369
430
  await assert.rejects(async () => {
370
431
  await executeCLI(
371
432
  'user',
372
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'query', '--body-file', bodyFile],
433
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'query', '--body-file', bodyFile],
373
434
  env as any,
374
435
  async () => new Response('{}', { status: 200 }),
375
436
  )
@@ -391,7 +452,7 @@ test('selection action rejects all selection for archive action', async () => {
391
452
  await assert.rejects(async () => {
392
453
  await executeCLI(
393
454
  'user',
394
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
455
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
395
456
  env as any,
396
457
  async () => new Response('{}', { status: 200 }),
397
458
  )
@@ -417,7 +478,7 @@ test('selection action rejects selectAll-only condition for archive action', asy
417
478
  await assert.rejects(async () => {
418
479
  await executeCLI(
419
480
  'user',
420
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
481
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
421
482
  env as any,
422
483
  async () => new Response('{}', { status: 200 }),
423
484
  )
@@ -441,7 +502,7 @@ test('insert entity rejects local file path for file field and points to arcubas
441
502
  await assert.rejects(async () => {
442
503
  await executeCLI(
443
504
  'user',
444
- ['workflow', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
505
+ ['rows', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
445
506
  env as any,
446
507
  async (url, init) => {
447
508
  calls.push({ url: String(url), method: init?.method })
@@ -490,7 +551,7 @@ test('update row rejects object value for image field and requires upload array'
490
551
  await assert.rejects(async () => {
491
552
  await executeCLI(
492
553
  'user',
493
- ['workflow', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
554
+ ['rows', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
494
555
  env as any,
495
556
  async (url, init) => {
496
557
  if (String(url) === 'https://arcubase.example.com/entity/app_1/entity_1' && init?.method === 'GET') {
@@ -535,7 +596,7 @@ test('insert entity accepts upload array for file field after schema preflight',
535
596
 
536
597
  const out = await executeCLI(
537
598
  'user',
538
- ['workflow', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
599
+ ['rows', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
539
600
  env as any,
540
601
  async (url, init) => {
541
602
  calls.push({ url: String(url), method: init?.method })
@@ -581,7 +642,7 @@ test('insert entity decorates upload bind failures with actionable guidance', as
581
642
  await assert.rejects(async () => {
582
643
  await executeCLI(
583
644
  'user',
584
- ['workflow', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
645
+ ['rows', 'insert-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
585
646
  env as any,
586
647
  async (url, init) => {
587
648
  if (String(url) === 'https://arcubase.example.com/entity/app_1/entity_1' && init?.method === 'GET') {
@@ -626,7 +687,7 @@ test('selection action rejects condition selection without selectAll', async ()
626
687
  await assert.rejects(async () => {
627
688
  await executeCLI(
628
689
  'user',
629
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
690
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
630
691
  env as any,
631
692
  async () => new Response('{}', { status: 200 }),
632
693
  )
@@ -653,7 +714,7 @@ test('selection action rejects condition selection that uses text_search instead
653
714
  await assert.rejects(async () => {
654
715
  await executeCLI(
655
716
  'user',
656
- ['workflow', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
717
+ ['rows', 'query-entity-selection', '--app-id', 'app_1', '--entity-id', 'entity_1', '--action', 'archive', '--body-file', bodyFile],
657
718
  env as any,
658
719
  async () => new Response('{}', { status: 200 }),
659
720
  )
@@ -780,7 +841,7 @@ test('query entity rejects camelCase viewMode and points to row crud doc', async
780
841
  await assert.rejects(async () => {
781
842
  await executeCLI(
782
843
  'user',
783
- ['workflow', 'query-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
844
+ ['rows', 'query-entity', '--app-id', 'app_1', '--entity-id', 'entity_1', '--body-file', bodyFile],
784
845
  env as any,
785
846
  async () => new Response('{}', { status: 200 }),
786
847
  )
@@ -807,7 +868,7 @@ test('update entity row rejects fields key', async () => {
807
868
  await assert.rejects(async () => {
808
869
  await executeCLI(
809
870
  'user',
810
- ['workflow', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
871
+ ['rows', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
811
872
  env as any,
812
873
  async () => new Response('{}', { status: 200 }),
813
874
  )
@@ -831,7 +892,7 @@ test('update entity row rejects changed_fields that are missing from data', asyn
831
892
  await assert.rejects(async () => {
832
893
  await executeCLI(
833
894
  'user',
834
- ['workflow', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
895
+ ['rows', 'update-entity-row', '--app-id', 'app_1', '--entity-id', 'entity_1', '--row-id', 'row_1', '--body-file', bodyFile],
835
896
  env as any,
836
897
  async () => new Response('{}', { status: 200 }),
837
898
  )
@@ -28,10 +28,11 @@ test('module help prints create-entity command', async () => {
28
28
  const out = await executeCLI('admin', ['app', '--help'], env as any, async () => new Response('{}'))
29
29
  assert.equal(out.kind, 'help')
30
30
  assert.match(out.text, /create-entity/)
31
+ assert.match(out.text, /inventory/)
31
32
  })
32
33
 
33
34
  test('module help does not require runtime env', async () => {
34
- const out = await executeCLI('user', ['workflow', '--help'], undefined, async () => new Response('{}'))
35
+ const out = await executeCLI('user', ['rows', '--help'], undefined, async () => new Response('{}'))
35
36
  assert.equal(out.kind, 'help')
36
37
  assert.match(out.text, /delete-entity-row/)
37
38
  })
@@ -44,13 +45,13 @@ test('command help prints normalized external endpoint', async () => {
44
45
  })
45
46
 
46
47
  test('command help does not require runtime env', async () => {
47
- const out = await executeCLI('user', ['workflow', 'delete-entity-row', '--help'], undefined, async () => new Response('{}'))
48
+ const out = await executeCLI('user', ['rows', 'delete-entity-row', '--help'], undefined, async () => new Response('{}'))
48
49
  assert.equal(out.kind, 'help')
49
50
  assert.match(out.text, /endpoint: \/entity\/:app_id\/:entity_id\/delete-item/)
50
51
  })
51
52
 
52
53
  test('command help prints runtime doc hints for request body commands', async () => {
53
- const out = await executeCLI('user', ['workflow', 'update-entity-row', '--help'], undefined, async () => new Response('{}'))
54
+ const out = await executeCLI('user', ['rows', 'update-entity-row', '--help'], undefined, async () => new Response('{}'))
54
55
  assert.equal(out.kind, 'help')
55
56
  assert.match(out.text, /docs:/)
56
57
  assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/row-crud\.md/)
@@ -58,7 +59,7 @@ test('command help prints runtime doc hints for request body commands', async ()
58
59
  })
59
60
 
60
61
  test('command help prints query-file support', async () => {
61
- const out = await executeCLI('user', ['workflow', 'query-entity', '--help'], undefined, async () => new Response('{}'))
62
+ const out = await executeCLI('user', ['rows', 'query-entity', '--help'], undefined, async () => new Response('{}'))
62
63
  assert.equal(out.kind, 'help')
63
64
  assert.match(out.text, /query flags: --query-file/)
64
65
  })
@@ -70,12 +71,19 @@ test('command help without request body can still point to lifecycle and example
70
71
  assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/examples\/README\.md/)
71
72
  })
72
73
 
74
+ test('app inventory help explains app discovery purpose', async () => {
75
+ const out = await executeCLI('admin', ['app', 'inventory', '--help'], undefined, async () => new Response('{}'))
76
+ assert.equal(out.kind, 'help')
77
+ assert.match(out.text, /list all accessible apps and each app entity list/)
78
+ assert.match(out.text, /use this before asking the user for app_id/)
79
+ })
80
+
73
81
  test('admin-save-entity help prints row-operation next step', async () => {
74
82
  const out = await executeCLI('admin', ['entity', 'admin-save-entity', '--help'], undefined, async () => new Response('{}'))
75
83
  assert.equal(out.kind, 'help')
76
84
  assert.match(out.text, /switch to arcubase for row operations/)
77
- assert.match(out.text, /arcubase workflow insert-entity/)
78
- assert.match(out.text, /arcubase workflow query-entity/)
85
+ assert.match(out.text, /arcubase rows insert-entity/)
86
+ assert.match(out.text, /arcubase rows query-entity/)
79
87
  })
80
88
 
81
89
  test('wrong admin workflow command points to arcubase row command', async () => {
@@ -83,9 +91,9 @@ test('wrong admin workflow command points to arcubase row command', async () =>
83
91
  () => executeCLI('admin', ['workflow', 'insert-entity'], env as any, async () => new Response('{}')),
84
92
  (error: any) => {
85
93
  assert.equal(error.code, 'UNKNOWN_COMMAND')
86
- assert.equal(error.details?.reason, 'workflow insert-entity is a row operation, not an arcubase-admin command')
87
- assert.equal(error.details?.hint, 'use: arcubase workflow insert-entity')
88
- assert.ok((error.details?.suggestions ?? []).includes('arcubase workflow insert-entity'))
94
+ assert.equal(error.details?.reason, 'workflow insert-entity is not a valid command group; use arcubase rows for row operations')
95
+ assert.equal(error.details?.hint, 'use: arcubase rows insert-entity')
96
+ assert.ok((error.details?.suggestions ?? []).includes('arcubase rows insert-entity'))
89
97
  return true
90
98
  }
91
99
  )
@@ -1,19 +0,0 @@
1
- # Workflow Runtime Reference
2
-
3
- This directory is reserved for future workflow runtime docs.
4
-
5
- It will follow the same single-path distribution model as entity schema docs:
6
-
7
- - Development source of truth:
8
- - `misc/packages/@carthooks/arcubase-cli/docs/runtime-reference/workflow/`
9
- - Runtime location:
10
- - `/opt/arcubase-sdk/docs/runtime-reference/workflow/`
11
- - Distribution path:
12
- - `sdk-dist/docs/runtime-reference/workflow/`
13
-
14
- Expected coverage later:
15
-
16
- - workflow configuration lifecycle
17
- - real writable structure for nodes, edges, actions, and conditions
18
- - integration points between workflow and entity schema
19
- - common failure cases and debugging guidance