@jinshuju/cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +311 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.js +184 -0
- package/dist/cli-bin.d.ts +2 -0
- package/dist/cli-bin.js +8 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +699 -0
- package/dist/commands.d.ts +84 -0
- package/dist/commands.js +1672 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +99 -0
- package/dist/help.d.ts +15 -0
- package/dist/help.js +98 -0
- package/dist/http.d.ts +29 -0
- package/dist/http.js +127 -0
- package/dist/options.d.ts +102 -0
- package/dist/options.js +232 -0
- package/dist/payload.d.ts +12 -0
- package/dist/payload.js +59 -0
- package/dist/progress.d.ts +16 -0
- package/dist/progress.js +17 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# @jinshuju/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for the Jinshuju Open API v1.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @jinshuju/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Two equivalent entry points are installed:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
jinshuju --help
|
|
15
|
+
jsj --help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Working on the CLI itself
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm link # jinshuju / jsj now resolve to your checkout
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If `npm link` leaves you with `permission denied`, `dist/cli-bin.js` has lost
|
|
26
|
+
its execute bit — `npm run build` restores it.
|
|
27
|
+
|
|
28
|
+
## Authentication
|
|
29
|
+
|
|
30
|
+
Three kinds of credential are supported: an access token (personal or account),
|
|
31
|
+
an API key and secret pair, and an interactive browser login.
|
|
32
|
+
|
|
33
|
+
Write them to the config file (`~/.jinshuju/config.json`, mode 600):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
jinshuju config set access_token xxx # access token
|
|
37
|
+
jinshuju config set api_key xxx # or API key / secret
|
|
38
|
+
jinshuju config set api_secret xxx
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Environment variables work just as well, which suits CI and scripts:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export JINSHUJU_ACCESS_TOKEN=xxx
|
|
45
|
+
# or
|
|
46
|
+
export JINSHUJU_API_KEY=xxx
|
|
47
|
+
export JINSHUJU_API_SECRET=xxx
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The **precedence** is access token, then API key and secret, then a stored
|
|
51
|
+
browser login (`jinshuju auth login`). An explicitly configured credential
|
|
52
|
+
always beats a stored session: setting a token and then acting as last week's
|
|
53
|
+
login is a surprise nobody wants. `jinshuju auth status` reports which
|
|
54
|
+
credential is in use and where it came from:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
$ jinshuju auth status
|
|
58
|
+
Authenticated with an access token (from env).
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Access tokens are masked in `config get` like any other secret; pass
|
|
62
|
+
`--show-secret` for the full value.
|
|
63
|
+
|
|
64
|
+
## Creating forms
|
|
65
|
+
|
|
66
|
+
Field types use the API v1 type names. Do not supply `api_code` when creating a
|
|
67
|
+
field — the server assigns it.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
jinshuju form create --json @form.json --scene registry --layout card --folder Fd2xK8
|
|
71
|
+
jinshuju form create --json @exam.json --type exam # the payload may carry exam_setting
|
|
72
|
+
jinshuju form edit Kp7mQ2 --json '{"exam_setting":{"limited_time":45}}'
|
|
73
|
+
|
|
74
|
+
jinshuju form create --json '{
|
|
75
|
+
"name": "Event signup",
|
|
76
|
+
"fields": [
|
|
77
|
+
{ "type": "TextField", "label": "Name", "required": true },
|
|
78
|
+
{ "type": "MobileField", "label": "Mobile", "required": true }
|
|
79
|
+
]
|
|
80
|
+
}'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`--type exam` and `--type evaluation` select both the scene and the settings
|
|
84
|
+
block that belongs to it. That settings block has an endpoint of its own, so a
|
|
85
|
+
payload carrying it becomes two requests — and the general form update does not
|
|
86
|
+
recognise the key at all (it reads only `name`, `description`, `setting`,
|
|
87
|
+
`fields` and `field_rules`), so without the dispatch `exam_setting` would be
|
|
88
|
+
dropped in silence.
|
|
89
|
+
|
|
90
|
+
The settings block is always sent first. It is the half that can be rejected —
|
|
91
|
+
"this is not an exam form" — and sending it first is what guarantees a rejection
|
|
92
|
+
leaves every other change unsent.
|
|
93
|
+
|
|
94
|
+
## Reading data
|
|
95
|
+
|
|
96
|
+
`entry`, `view`, `field` and `comment` are all top-level resources; the
|
|
97
|
+
container is given by `--form` or `--table` (mutually exclusive, both mapping to
|
|
98
|
+
the API's `form_token`).
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
jinshuju entry list --form Kp7mQ2
|
|
102
|
+
jinshuju entry list --table Vn4xR8
|
|
103
|
+
jinshuju entry get 1 --form Kp7mQ2
|
|
104
|
+
jinshuju entry list --form Kp7mQ2 --view aB3dE9
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Filtering, sorting and paging:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
jinshuju entry list --form Kp7mQ2 --filter 'field_3 gte 80'
|
|
111
|
+
jinshuju entry list --form Kp7mQ2 --filter 'created_at within_last 30d' --filter 'field_9 not_null'
|
|
112
|
+
jinshuju entry list --form Kp7mQ2 --sort created_at:desc
|
|
113
|
+
jinshuju entry list --form Kp7mQ2 --limit 10
|
|
114
|
+
jinshuju entry list --form Kp7mQ2 --all
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--limit` can only ask for less: a listing's default page size is also its
|
|
118
|
+
maximum (50 in most cases), and anything larger is capped.
|
|
119
|
+
|
|
120
|
+
`--filter` is repeatable and the conditions are AND-combined. For conditions it
|
|
121
|
+
cannot express, use `--filters <json|@file>`. Cursors are opaque strings — pass
|
|
122
|
+
the `next` value from the previous response back verbatim.
|
|
123
|
+
|
|
124
|
+
## Analysis
|
|
125
|
+
|
|
126
|
+
There is no need to pull rows out and count them yourself. Counts, aggregates
|
|
127
|
+
and profiles are computed server-side, and the size of the response depends only
|
|
128
|
+
on how many metrics you asked for and how many groups came back.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
jinshuju entry count --form Kp7mQ2 --filter 'field_3 gte 80'
|
|
132
|
+
jinshuju entry count --form Kp7mQ2 --form Vn4xR8 # repeatable, up to 10 containers
|
|
133
|
+
|
|
134
|
+
jinshuju entry aggregate --form Kp7mQ2 --metric avg:field_3
|
|
135
|
+
jinshuju entry aggregate --form Kp7mQ2 --metric count:field_1 --by created_at:month --limit 12
|
|
136
|
+
|
|
137
|
+
jinshuju entry summary --form Kp7mQ2
|
|
138
|
+
jinshuju entry summary --form Kp7mQ2 --fields field_3,field_7 --no-overview
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`--metric <func>:<field>` is repeatable, 1–20 of them; `--by <field>[:day|week|month]`
|
|
142
|
+
takes at most 2, and a date dimension must name a bucket. Which functions a
|
|
143
|
+
field accepts is the field's own answer — read `analytics.agg_funcs` from
|
|
144
|
+
`form get`.
|
|
145
|
+
|
|
146
|
+
A multi-container count takes no `--keyword`, and its `--filter` is limited to
|
|
147
|
+
`created_at`, `updated_at` and `creator_id`: the same api_code names a different
|
|
148
|
+
field in every container, so comparing across them would mean nothing.
|
|
149
|
+
|
|
150
|
+
## Creating entries
|
|
151
|
+
|
|
152
|
+
Payload keys are field `api_code`s, not field labels. `--json` accepts inline
|
|
153
|
+
JSON, `@file`, and `-` for stdin.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
jinshuju entry create --form Kp7mQ2 --json '{
|
|
157
|
+
"field_1": "Alice",
|
|
158
|
+
"field_2": "13800138000"
|
|
159
|
+
}'
|
|
160
|
+
|
|
161
|
+
jinshuju entry create --form Kp7mQ2 --json @entry.json
|
|
162
|
+
cat entry.json | jinshuju entry create --form Kp7mQ2 --json -
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Writing
|
|
166
|
+
|
|
167
|
+
Apart from the settings-block dispatch described above, every write command
|
|
168
|
+
issues exactly one request, and payload keys are field `api_code`s.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
jinshuju folder create Ledgers --kind table # folders are form or table; a table cannot go in a form folder
|
|
172
|
+
jinshuju form edit Kp7mQ2 --json '{"name":"2026 signups"}'
|
|
173
|
+
jinshuju form copy Kp7mQ2 --name Copy
|
|
174
|
+
jinshuju form move Kp7mQ2 --folder Fd2xK8 # without --folder, moves it out of its folder
|
|
175
|
+
jinshuju form theme set Kp7mQ2 --primary-color "#1F6FEB"
|
|
176
|
+
jinshuju table create --json @table.json --folder Nf7mDC
|
|
177
|
+
jinshuju table edit Vn4xR8 --json '{"name":"2026 ledger"}'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Adding, changing and removing fields all land on a single PATCH of the
|
|
181
|
+
container:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
jinshuju field add --form Kp7mQ2 --json '{"type":"TextField","label":"Notes"}'
|
|
185
|
+
jinshuju field update --form Kp7mQ2 field_3 --json '{"required":true}'
|
|
186
|
+
jinshuju field update-choices --form Kp7mQ2 field_7 --json '{"add":[{"label":"Third"}]}'
|
|
187
|
+
jinshuju field remove --form Kp7mQ2 field_9 --yes
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Data and views:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
jinshuju entry create --form Kp7mQ2 --batch @entries.json
|
|
194
|
+
jinshuju entry update --form Kp7mQ2 12 --json '{"field_2":99}' # merge
|
|
195
|
+
jinshuju entry update --form Kp7mQ2 12 --replace --json '{"field_1":"Bob"}' # replace; omitted fields are cleared
|
|
196
|
+
jinshuju entry update --form Kp7mQ2 --batch @rows.json # [{serial_number, entry}]
|
|
197
|
+
jinshuju entry delete --form Kp7mQ2 12 --yes
|
|
198
|
+
|
|
199
|
+
jinshuju view create --form Kp7mQ2 "High scores" --filter 'field_3 gte 80' --sort created_at:desc
|
|
200
|
+
jinshuju comment create --form Kp7mQ2 --entry 12 "Contacted, awaiting reply"
|
|
201
|
+
jinshuju opensearch edit Qy7nR3 --disable
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Deletion always requires `--yes`. This CLI is non-interactive — stdin belongs to
|
|
205
|
+
`--json -` — so confirmation is a flag: without it nothing is deleted, rather
|
|
206
|
+
than a prompt nobody is there to answer.
|
|
207
|
+
|
|
208
|
+
## Searching across containers
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
jinshuju entry search "Acme Corp" # every form and table you can read, up to 10
|
|
212
|
+
jinshuju entry search 13800138000 --form Kp7mQ2 --form Vn4xR8
|
|
213
|
+
jinshuju entry search repair --scope-filter 'entries_count gt 100' # picks which forms to search, not which rows
|
|
214
|
+
|
|
215
|
+
jinshuju entry stats --from 2026-09-01 # how much each form received in the period
|
|
216
|
+
jinshuju entry stats --from 2026-09-01 --to 2026-09-07 --kind form --limit 10
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
A form that could not be searched stays in the result **with the reason**,
|
|
220
|
+
rather than being dropped as if it had matched nothing — "not searched" and
|
|
221
|
+
"nothing there" are different answers.
|
|
222
|
+
|
|
223
|
+
`entry stats` and `entry count` measure different things: the former is how much
|
|
224
|
+
arrived (an import counts on the day it ran, and deletions are not subtracted),
|
|
225
|
+
the latter is how much is there now.
|
|
226
|
+
|
|
227
|
+
## Your own submissions
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
jinshuju form list --mine # forms you filled in, not forms you own
|
|
231
|
+
jinshuju entry list --form Kp7mQ2 --mine # what you submitted to this form
|
|
232
|
+
jinshuju entry search "Acme Corp" --mine # search your own submissions
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The scope is pinned to your own submissions: you cannot read anyone else's, and
|
|
236
|
+
you need no permission on the form itself. Flags that only make sense from an
|
|
237
|
+
owner's point of view (`--sort`, `--view`, `--scope-filter` and the like) are
|
|
238
|
+
rejected when combined with `--mine`.
|
|
239
|
+
|
|
240
|
+
## Working with files
|
|
241
|
+
|
|
242
|
+
Three commands take a file. The CLI uploads with its own credential; there is no
|
|
243
|
+
ticket to fetch first.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
jinshuju entry import --form Kp7mQ2 ./signups.xlsx --map field_1=Name --map field_2=Mobile
|
|
247
|
+
jinshuju entry import --table Vn4xR8 ./rows.csv --map field_1=1 --map field_2=2 --header-row 2 --unique field_1
|
|
248
|
+
|
|
249
|
+
jinshuju entry create --form Kp7mQ2 --json '{"field_1":"Alice"}' --attach field_5=./id-card.jpg
|
|
250
|
+
jinshuju form theme set Kp7mQ2 --wallpaper ./bg.png
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
jinshuju entry import --form Kp7mQ2 ./signups.xlsx --map field_1=Name --wait # wait for it; non-zero exit on failure
|
|
255
|
+
jinshuju entry import-status --form Kp7mQ2 <job-id> # or look it up later
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`--map` takes a field api_code on the left and a **column name or column
|
|
259
|
+
number** on the right (a bare number is read as a position). Everything knowable
|
|
260
|
+
up front is checked before the import starts — the file, the size your plan
|
|
261
|
+
allows, the header row, the column mapping — so a rejected import has written
|
|
262
|
+
nothing, and the error names the sheet's actual layout. Once accepted, the rows
|
|
263
|
+
are written in the background: the command returning means **started**, not
|
|
264
|
+
finished.
|
|
265
|
+
|
|
266
|
+
That is what `--wait` is for. It waits for the job to settle, reports how many
|
|
267
|
+
rows were written, skipped and rejected, and **exits non-zero on failure**.
|
|
268
|
+
Without it a failed import is invisible — the command succeeds and not a single
|
|
269
|
+
row is written. After the fact, `entry import-status` answers the same question
|
|
270
|
+
from a job id.
|
|
271
|
+
|
|
272
|
+
## Progress
|
|
273
|
+
|
|
274
|
+
Long-running commands (`--all` paging, uploads, `--wait`) report progress.
|
|
275
|
+
Progress is written **to stderr, and only when stderr is a terminal**, so
|
|
276
|
+
`--output json | jq` receives exactly the same bytes it would without it: a pipe,
|
|
277
|
+
or an agent on the other end, never sees a stray character.
|
|
278
|
+
|
|
279
|
+
## Before removing a field
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
jinshuju field check --form Kp7mQ2 field_3 field_7:choice_1
|
|
283
|
+
jinshuju field preview-convert --form Kp7mQ2 field_1 --to RadioButton
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`field check` answers whether a field or a choice has data under it — removing
|
|
287
|
+
it takes that data along, so it is worth asking first. `preview-convert` reports
|
|
288
|
+
how much a type conversion would keep and how much it would clear.
|
|
289
|
+
|
|
290
|
+
## Odds and ends
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
jinshuju form list --name signup --name survey # several keywords match any, not one joined phrase
|
|
294
|
+
jinshuju entry list --form Kp7mQ2 --labels # carry the field label with each value, saving a form read
|
|
295
|
+
jinshuju table move Vn4xR8 --folder Nf7mDC # a table only goes into a kind=table folder
|
|
296
|
+
jinshuju table create --json @t.json --with-default-entries # seed a few blank rows, as the web UI does
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Token format
|
|
300
|
+
|
|
301
|
+
Form, table and view tokens are **six characters of mixed-case letters and
|
|
302
|
+
digits**, for example `Kp7mQ2`, `Vn4xR8`, `aB3dE9`. Examples here and in
|
|
303
|
+
`--help` use that shape throughout.
|
|
304
|
+
|
|
305
|
+
## Development
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
npm install
|
|
309
|
+
npm test
|
|
310
|
+
npm run typecheck
|
|
311
|
+
```
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type LoadConfigOptions, type LoadedConfig, type OAuthConfig } from './config.js';
|
|
2
|
+
export type TokenResponse = {
|
|
3
|
+
access_token: string;
|
|
4
|
+
refresh_token?: string;
|
|
5
|
+
expires_in?: number;
|
|
6
|
+
scope?: string;
|
|
7
|
+
token_type?: string;
|
|
8
|
+
};
|
|
9
|
+
export type OAuthLoginOptions = LoadConfigOptions & {
|
|
10
|
+
host?: string;
|
|
11
|
+
authHost?: string;
|
|
12
|
+
clientId?: string;
|
|
13
|
+
scopes?: string;
|
|
14
|
+
openBrowser?: boolean;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
port?: number;
|
|
17
|
+
};
|
|
18
|
+
export type OAuthLoginResult = {
|
|
19
|
+
authorizeUrl: string;
|
|
20
|
+
callbackUrl: string;
|
|
21
|
+
token: OAuthConfig;
|
|
22
|
+
};
|
|
23
|
+
export type BrowserOpener = (url: string) => Promise<void>;
|
|
24
|
+
export declare function createPkcePair(): {
|
|
25
|
+
verifier: string;
|
|
26
|
+
challenge: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function buildAuthorizationUrl(params: {
|
|
29
|
+
authHost: string;
|
|
30
|
+
clientId: string;
|
|
31
|
+
redirectUri: string;
|
|
32
|
+
scope: string;
|
|
33
|
+
state: string;
|
|
34
|
+
codeChallenge: string;
|
|
35
|
+
}): string;
|
|
36
|
+
export declare function loginWithOAuth(options?: OAuthLoginOptions, opener?: BrowserOpener): Promise<OAuthLoginResult>;
|
|
37
|
+
export declare function refreshOAuthToken(config: LoadedConfig): Promise<OAuthConfig>;
|
|
38
|
+
export declare function revokeOAuthToken(config: LoadedConfig): Promise<void>;
|
|
39
|
+
export declare function shouldRefresh(auth: OAuthConfig, skewMs?: number): boolean;
|