@openengio/cli 0.0.0-stage → 0.24.3

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 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 CHANGED
@@ -1,3 +1,413 @@
1
- # Temporary Holding Version
1
+ # @openengio/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `openeng` is the platform's command-line client. It signs a person in through the browser, stores the
4
+ session, and calls the platform's host. It runs on Node 22 and has no argument-parser dependency
5
+ (`node:util`'s `parseArgs` serves because every command takes the same few flags).
6
+
7
+ ## Install
8
+
9
+ Install the public npm package:
10
+
11
+ ```
12
+ npm install -g @openengio/cli
13
+ ```
14
+
15
+ `openeng` has no host built in. The production host is `https://api.openeng.io`. Pass `--host
16
+ https://api.openeng.io` to a command, or set `OPENENG_HOST=https://api.openeng.io` once to make it the
17
+ default. An agent starts with `openeng skill`, which prints how to work with the platform.
18
+
19
+ `openeng --version` prints the published version, `0.<protocol>.<patch>`. `openeng organisations` and
20
+ `openeng orgs` run the commands of `openeng org`, and `openeng project` runs the commands of `openeng projects`.
21
+
22
+ `openeng help <command words>` prints one command's help, with or without the positional arguments the
23
+ command takes: a description (`does:`), an example and the shape of its output. `openeng help <group>`,
24
+ such as `openeng help connector`, lists the group's commands. Words that name no command are refused
25
+ with the nearest commands.
26
+
27
+ The package version is `0.<protocol>.<patch>`: its middle number is the host protocol it speaks.
28
+ The installed package contains the bundled command and the skill. It carries no runtime dependencies.
29
+
30
+ ## Commands
31
+
32
+ | command | what it does | host calls |
33
+ |---|---|---|
34
+ | `openeng login [--no-open]` | binds a loopback listener, opens the browser at the sign-in address (or prints it with `--no-open`), redeems the returned code once and stores the session | one (`redeem`) |
35
+ | `openeng logout` | refreshes first when the access token has expired, calls `logout`, deletes the stored session whatever the host answered | one, two with an expired token |
36
+ | `openeng whoami` | prints the principal, host, store and expiry of the stored session, and the client's protocol version (`protocolVersion` under `--json`); never a token | none |
37
+ | `openeng org list` | lists the organisations the signed-in person holds a live grant in; needs no attachment | one |
38
+ | `openeng org attach <id>` | attaches the session to the organisation, and stores the access token the host answers with the organisation's identifier and name, beside the session and under the same host key; needs no attachment | one |
39
+ | `openeng org create --name <name> [--idempotency-key <key>]` | creates an organisation without an attachment; makes one host call, and prints a minted idempotency key on standard error when none is supplied | one |
40
+ | `openeng org detach` | detaches the session, and stores the access token the host answers with a null organisation; refused while the session has none | one |
41
+ | `openeng org current` | prints the organisation the stored session is attached to, or none (`{ "organisation": null }` under `--json`); sends no request | none |
42
+ | `openeng projects list` | lists the projects the signed-in person may list | one |
43
+ | `openeng demo seed --project <id> [--size standard\|full]` | plays a generic open-cycle gas turbine plant into a project the person may list; `standard` is the default size, and a repeat completes what is left | one listing, one catalogue read, four to publish the types when they are not yet published, then one per object and edge |
44
+ | `openeng link <explore\|branch-diff\|project> --project <id> --ref <ref>`, `openeng link <ontology\|change-set> --ref <ref>`, `openeng link <object\|type\|proposal\|app> <identifier> --project <id> --ref <ref>`, `openeng link app-page <appKey> <pageKey> --project <id> --ref <ref>`, `openeng link app-draft <appKey> --ref <ref>`, `openeng link organisation-page <page>` | prints the address of a page of the web application: the host's web origin joined to the page's path, built for the attached organisation; `--json` answers `{ link }`; a flag the page does not read or a ref outside `main` and `<projectId>/<branch>` is refused (exit 2) before any call; a branch must match `--project` when given. The command reads the ref and the page's object, catalogue type, app page or proposal before printing, and refuses a missing subject with no address; a host with no web origin is refused too. `openeng link --help` prints every form | one `readWebOrigin`, plus a ref read for a branch or a project page on main, plus the subject read for object, type, app, app-page or proposal; the reads run in parallel |
45
+ | `openeng types draft open` | opens a change set on the attached organisation's namespace | one |
46
+ | `openeng projects create --name <text>` | creates a project in the organisation the session is attached to, under a minted idempotency key | one |
47
+ | `openeng types list --project <id>` | lists the project's published types | one |
48
+ | `openeng types draft current` | the attached organisation's open change set, or none | one |
49
+ | `openeng types draft show --change-set <id> [--project <id>]` | the change set's revision and each prospective type's full definition; with `--project`, the delta per type and per field instead (added or changed, the version before and after, each changed field's before and after), and `--json` answers `{ revision, changes, delta }` (with `railGroups` where the change set holds a group change); a line `rail groups <json array>` lists the change set's groups where it holds any; a project whose catalogue listing (category, key and version) is not the change set's is refused (exit 2) before any diff | one; two with `--project` |
50
+ | `openeng types export --project <id>` | prints the project's published types as one JSON object keyed by type key, each the first prospective version of an edit (the next version number, none of the host's bookkeeping); redirect it to a file and edit it for `openeng types draft revise --from`; on an empty catalogue it still prints `{}` and exits 0, with the standard-error note `no published types; start from the example in openeng help ontology-sdk reviseChangeSet` | one |
51
+ | `openeng types draft state --change-set <id>` | whether the change set is open, accepted or discarded | one |
52
+ | `openeng types draft revise --change-set <id> --revision <token> --from <file> [--rail-groups <json>]` | replaces the change set's delta with the JSON object in the file, keyed by type key; a type's rail placement is `presentationBindings.rail` inside its document; `--rail-groups` carries the organisation's whole list of rail groups as a JSON array of strings, and without it the change set keeps the groups its last revision held; a file that is unreadable, not JSON or not an object, or a flag that is not a JSON array of strings, is refused (exit 2) before any call; the host validates the delta | one |
53
+ | `openeng types draft validate --change-set <id> --revision <token>` | the host's violations for that revision, printed with their count; exits 4 when there is one or more and 0 when there are none, so a script can gate `accept` on the exit code | one |
54
+ | `openeng types draft discard --change-set <id>` | discards the change set | one |
55
+ | `openeng types draft accept --change-set <id> --revision <token>` | publishes the change set's types at the revision the person reviewed, under a minted idempotency key | one |
56
+ | `openeng rules add` | adds a project or organisation ref rule | one |
57
+ | `openeng rules list` | lists the selected project's or organisation's rules | one |
58
+ | `openeng rules remove` | removes a rule by identifier | one |
59
+ | `openeng org-refs create` | creates an organisation ref, optionally read-only | one |
60
+ | `openeng org-refs list` | lists visible organisation refs | one |
61
+ | `openeng org-refs delete` | deletes a writable organisation ref | one |
62
+ | `openeng org-refs mark-read-only` | marks an organisation ref read-only | one |
63
+ | `openeng locks set` | sets or lifts an object's lock on a project ref | one |
64
+ | `openeng ceiling show` | reads a project's maximum agent role | one |
65
+ | `openeng ceiling set` | sets a project's maximum agent role | one |
66
+ | `openeng schema-update request` | records a schema update request | one |
67
+ | `openeng schema-update show` | reads the latest visible request, or no request | one |
68
+ | `openeng skill [<topic> \| playbook <name>]` | prints the main skill and lists topics, or prints one bundled topic; needs no host and no session | none |
69
+
70
+ An object type's `subscriptionRoles` field declares which roles it admits: owner, approver or watcher.
71
+ Owner is limited to one; approver and watcher may have optional maxima. Without the field, only
72
+ watchers are admitted.
73
+
74
+ ## The rules commands
75
+
76
+ These commands use the Admin SDK. Choose exactly one of `--project <id>` and `--organisation`.
77
+ The boolean `--organisation` flag takes no value and uses the session's attached organisation;
78
+ its calls have no selector. Both flags together, or neither, refuse before a host call (exit 2).
79
+ The project forms are:
80
+
81
+ - `openeng rules add --project <id> --pattern <pattern> --consequence <merge-only|locked-rows-only>`
82
+ prints the rule's identifier, pattern and consequence. Repeating the same content returns the
83
+ existing rule, including when the pattern differs only in letter case.
84
+ - `openeng rules list --project <id>` prints each visible rule's identifier, pattern and
85
+ consequence, or `no rules` when there are none.
86
+ - `openeng rules remove --project <id> --rule-id <id>` prints the removed rule's identifier.
87
+ An absent or unreadable identifier, or one from another project, answers `not-found`.
88
+
89
+ Each command makes one host call, or two if the session first needs refreshing, and sends no
90
+ idempotency key. Under `--json`, each success answers `{ scope, value }`: `scope` is the CLI's
91
+ organisation and, for project forms, project; `value` holds the full rule for add, an array of rules for list, and
92
+ `{ ruleId }` for remove. Organisation forms call `addOrganisationRule`, `listOrganisationRules`
93
+ and `removeOrganisationRule` with the same inputs. Their full rule has `scope: "organisation"`
94
+ and `projectId: null`; project rules keep `scope: "project"`. That scope stays inside `value`.
95
+ Missing flags or a consequence other than the two spellings above refuse before any host call (exit 2).
96
+ The host validates the pattern.
97
+
98
+ A project rule's pattern is `main` or a ref under that project's identifier, exactly or ending
99
+ in `/*` to match the prefix. A matching `merge-only` rule requires changes through a branch and
100
+ merge. A `locked-rows-only` rule binds merge-only row locks while leaving unlocked rows direct.
101
+ Adding or removing rules needs a person's `administer projects` capability on the selected
102
+ project, or in the attached organisation for `--organisation`; both are audited. Listing needs
103
+ `read`. Each command's `--help` has a capability line. A `not-permitted` refusal carries no
104
+ capability; read that help line to find what the person needs. A change takes effect on the next
105
+ request. A `protected` write refusal means to use the branch and review workflow.
106
+ Agents must ask a person to manage rules.
107
+
108
+ ## Organisation refs, locks, agent ceilings and schema requests
109
+
110
+ All these commands need an attached organisation, make one host call (plus a refresh when needed),
111
+ and send no idempotency key. Text output is described below; `--json` always returns `{ scope, value }`
112
+ with the full route response in `value`, including null. A host refusal uses the standard explanation
113
+ and next step. Changing rules, refs and locks is a person's action, never a way for an agent to bypass
114
+ protection.
115
+
116
+ Creating, deleting or marking organisation refs needs `administer projects` in the attached
117
+ organisation. Setting a ceiling needs `administer projects` on the selected project; setting
118
+ or lifting a lock needs `lock objects` there. Requesting a schema update needs `write ref` on
119
+ a branch's home project, or `administer projects` for an organisation ref. Rule and organisation-ref
120
+ lists and ceiling reads need `read`. Schema-update show reads only the caller's own requests in the
121
+ token's organisation, without requiring project read access. Each command's `--help` names its requirement.
122
+ A branch whose name contains `/` cannot request or show a schema update today.
123
+
124
+ - `openeng org-refs create --name <name> [--read-only]` calls `createOrganisationRef` and prints
125
+ `ref <name> read-only` or `ref <name> writable`. It creates from the organisation's trunk head.
126
+ The name is one non-reserved ref segment, such as `release-v1`; without the flag it is writable.
127
+ - `openeng org-refs list` calls `listOrganisationRefs` and prints each ref in that form, or `no refs`.
128
+ - `openeng org-refs delete --name <name>` calls `deleteOrganisationRef` and prints `deleted ref <name>`.
129
+ A read-only ref cannot be deleted.
130
+ - `openeng org-refs mark-read-only --name <name>` calls `markOrganisationRefReadOnly` and prints the
131
+ ref's name and read-only state. All four organisation-ref calls have no selector.
132
+ - `openeng locks set --project <id> --ref <ref> --object <objectId> --lock <none|merge-only|frozen>`
133
+ calls the Objects client's `setObjectLock` with selector `{ projectId, ref }` and input
134
+ `{ objectId, lock }`. It prints `object <objectId> lock <lock>`. `none` lifts the lock;
135
+ the lock also applies to the object's child edges.
136
+ - `openeng ceiling show --project <id>` calls `readAgentCeiling` and prints `ceiling <role>`.
137
+ - `openeng ceiling set --project <id> --ceiling <contributor|maintainer>` calls `setAgentCeiling`
138
+ and prints `ceiling <role>`. Both ceiling calls use a project selector.
139
+ - `openeng schema-update request --ref <ref>` calls the Branches client's `requestSchemaUpdate`
140
+ and prints `schema update <ref> <state>`. It records a request; execution is separate.
141
+ - `openeng schema-update show --ref <ref>` calls `readSchemaUpdateRequest` and prints that same
142
+ form, or `no request` for null. Schema calls have no selector and act in the attached organisation.
143
+ The ref is a project branch or a bare organisation ref name; `main` is refused by the host.
144
+
145
+ Missing required flags and unknown lock or ceiling spellings refuse before any call (exit 2).
146
+ For an uncertain outcome, rules, organisation refs, ceiling and schema requests name a read-only
147
+ command: list the rules or organisation refs, or show the ceiling or schema request. Inspect that
148
+ answer before acting again. The lock command instead names the same idempotent `openeng locks set`
149
+ command with the same arguments, which sets the same lock again.
150
+
151
+ ## The connector commands
152
+
153
+ A connection imports an Onshape account's documents into a mirror project of its own. Every
154
+ connector command acts with the signed-in person's user token, so it can do what that person may,
155
+ and it needs the organisation the session is attached to. Each command's `--help` names the
156
+ capability it needs.
157
+
158
+ Everyone with access to this project sees every document this Onshape account can see.
159
+
160
+ `openeng connector create` prints that sentence, on standard error, before it makes any call.
161
+ `openeng connector set-credential` prints it again with the account identity the key resolved to.
162
+
163
+ | command | what it does | what the caller must hold |
164
+ |---|---|---|
165
+ | `openeng connector create --system onshape --stack <stack> --name <text> [--company <id>] [--team <id> \| --owner <id>] [--label <text>]` | creates the connection, its mirror project and its service identity, then drafts the connector's types as a change set in the same request; `--name` names the mirror project; `--stack` is the Onshape stack label, the host-name label joined to the suffix, as in `cad`; `--team` or `--owner` limits the import to that team's or owner's documents, and with neither the company's documents are imported, and `--company` is then required and must be the company's own identifier, not a user's, or the command is refused (exit 2) before any call; `--team` with `--owner` is refused (exit 2) before any call. The answer says whether the types were drafted, and if not, which rule stopped it; the connection exists either way | `administer projects` and `administer service identities` in the organisation, which the `owner` role holds |
166
+ | `openeng connector set-credential <connection>` | asks for the Onshape access key and secret key at a terminal prompt with nothing echoed, has the host check them against Onshape, and stores them; no flag or environment variable carries a key, and with no terminal it refuses before any call. Backspace deletes a character, a paste of both keys with a line ending between them fills both prompts, and Control-C or Control-D cancels with a refusal that says no keys were set. Setting it again replaces the stored credential. It prints the Onshape account the key belongs to and the time it was set | `administer projects` in the organisation |
167
+ | `openeng connector draft-types <connection>` | drafts the connector's types as a change set for a person to review, or answers that they are already approved or that a change set is open; use it for a connection whose drafting failed at create, and after a connector update | `administer types` in the organisation, and read on the mirror project |
168
+ | `openeng connector run <connection>` | starts an import run and prints its identifier once its row has committed; the run refuses while the connector's types are not approved | `merge` on the mirror project |
169
+ | `openeng connector runs <connection>` | lists the connection's runs, newest first: state, start and finish times, whether it was complete, the counts (created, updated, unchanged, withdrawn, carried, overwritten) and, for a failed run, its failure kind and fixed text | read on the mirror project |
170
+ | `openeng connector revoke <connection>` | revokes the connection: its running runs end as failed and its service identity can no longer act | `administer projects` and `administer service identities` in the organisation |
171
+
172
+ `<connection>` is the connection identifier that `create` prints.
173
+
174
+ ### From no connection to a first imported document
175
+
176
+ 1. `openeng connector create ...`, by an organisation owner. Note the connection identifier.
177
+ 2. `openeng connector set-credential <connection>`, by a person who holds `administer projects`, at a
178
+ terminal.
179
+ 3. A person who holds `administer types` reviews the drafted change set with `openeng types draft show
180
+ --change-set <id> --project <mirror project id>` and accepts it with `openeng types draft accept
181
+ --change-set <id> --revision <token>`. The change set identifier is in `create`'s answer, or in
182
+ `openeng types draft current`. Until it is accepted, a run refuses with `types-not-approved`.
183
+ 4. `openeng connector run <connection>`, by a person who holds `merge` on the mirror project.
184
+ 5. `openeng connector runs <connection>` until the run's state is `succeeded` or `failed`.
185
+
186
+ ### Owned records
187
+
188
+ The connected system alone writes records of its owned types. A person's write is refused as
189
+ `owned`. People put their own information in separate rows and link those rows to the mirrored
190
+ records. The importer counts updates to mirrored properties as `overwritten`. If a mirrored
191
+ record is absent, the importer creates it with a new identifier and counts it as `created`.
192
+
193
+ ## The attached organisation
194
+
195
+ A CLI session begins unattached: `openeng login` never attaches. A person runs `openeng org list`, then
196
+ `openeng org attach <id>`, once. If the list is empty, `openeng org create --name <name>` creates an
197
+ organisation that can then be attached. The organisation is stored with the
198
+ session (`StoredEntry.organisation`, an identifier and a name, or null), a refresh keeps it, and
199
+ `openeng login` and `openeng logout` replace or delete the whole entry. The host decides what a call may
200
+ reach; the stored organisation decides only what the CLI prints and whether it runs a command.
201
+
202
+ - **The gate.** Whether a command runs without an organisation comes from its `organisation` field:
203
+ hand-written commands marked `none` run unattached; generated commands run unattached when their
204
+ route subject is `platform` or `creating`. Other commands refuse with "not attached to an
205
+ organisation" (exit 2) before they send a request. With no stored session at all, the command
206
+ answers that it is not signed in.
207
+ - **The scope echo.** The CLI passes no scope when the organisation gate read no organisation
208
+ (for every command whose `organisation` is `none` and every hostless command), and for `org
209
+ detach`. Other commands print when the host answers them, `org: <name> (<id>)` first on standard error,
210
+ then ` · project: <id>` when `--project` or the selector's `projectId` named one, and
211
+ ` · ref: <ref>` when the selector named one. No host call finds them.
212
+ - **`--json`.** The one value on standard output gains the same fields as `scope`:
213
+ `{ "organisation": { "id", "name" }, "project"?, "ref"? }`. An object value normally gains a `scope` key. A
214
+ value that is an array or null is wrapped as `{ "scope": {...}, "value": <the original> }`. A
215
+ rules command's success always uses that wrapper to preserve the rule's own `scope` field. A
216
+ failure the host did not answer carries no scope. A command with no scope has no `scope` field.
217
+ - **The hint.** Every `not-found` the host answers to a gated command prints, after the refusal,
218
+ "not found in organisation <name>: check that you are attached to the correct organisation (openeng org
219
+ current)", and one line more when the command named a ref: `ref forms: the trunk is the bare "main";
220
+ a branch is "<project id>/<name>"`. The hint goes to standard error with or without `--json`.
221
+
222
+ A command that reaches the host with a session whose access token has expired sends one `refresh`
223
+ first and then its own call. A command whose call is answered `unauthenticated` refreshes once and
224
+ sends the call once more.
225
+
226
+ ## The agent skill
227
+
228
+ `skill/openeng/SKILL.md` is a Claude Code skill that tells an agent which commands to run and in what
229
+ order: sign in and orient, define types, write data, and show it. It names commands and never an
230
+ input shape; for a shape it sends the agent to `openeng help <module> <method>`. `openeng skill` prints it
231
+ and ends with the other topics and the command for each. It reads from the package relative to
232
+ the module, so it works from `dist`. Install it with:
233
+
234
+ ```
235
+ mkdir -p ~/.claude/skills/openeng && openeng skill > ~/.claude/skills/openeng/SKILL.md
236
+ ```
237
+
238
+ `openeng skill` is answered before a host is parsed or a session store is chosen, so it runs on a
239
+ machine with no `OPENENG_HOST`. Its command receives only the topic words, which is how it cannot reach a host.
240
+
241
+ The topic list comes from the files in the bundled `skill/` folder. Print a topic with:
242
+
243
+ | command | when to read it |
244
+ |---|---|
245
+ | `openeng skill demo` | guiding a new person through a hands-on example in the app |
246
+ | `openeng skill new-use-case` | designing types for a new use case against the existing catalogue |
247
+ | `openeng skill build-ui` | laying out an object drawer or App view |
248
+ | `openeng skill conventions` | field meanings, direction, trees, views, the rail and versions |
249
+ | `openeng skill playbook engineering-requirements` | modelling requirements, verification and risk |
250
+ | `openeng skill playbook product-lifecycle` | modelling parts, revisions, manufacturing BOMs and changes |
251
+ | `openeng skill playbook project-management` | modelling work items, cycles, milestones and engineering links |
252
+
253
+ `openeng help skill` and `openeng skill --help` list the topics. Unknown topics are refused with that
254
+ list (exit 2). The material requirements planning draft is held back and is not shipped.
255
+ With `--json`, the main command prints `{ "skill": "...", "topics": ["..."] }`; a topic prints
256
+ `{ "skill": "..." }`, and help uses the CLI's usual `{ "usage": ["..."] }` shape.
257
+ The `build-ui` guide ships as prose; the in-app agent's generated `author-views` reference is
258
+ not yet available through the CLI.
259
+
260
+ ## The generated commands
261
+
262
+ Beside the commands above, `openeng` has one command for each route the SDK modules export an entry
263
+ for, built when the process starts from those entries (`OBJECTS_ROUTE_ENTRIES`,
264
+ `BRANCHES_ROUTE_ENTRIES`, `ADMIN_ROUTE_ENTRIES`, `AGENTS_ROUTE_ENTRIES` and
265
+ `ONTOLOGY_ROUTE_ENTRIES`). No list of commands, help text or flags is kept by hand.
266
+
267
+ - **Naming.** `openeng <module> <method>`, the module as its package is named (`objects-sdk`) and the
268
+ method the route's own name (`openeng objects-sdk readObject`).
269
+ - **Which routes.** Every entry whose `carriesCredential` is false and whose `module` is not
270
+ `sdk-core`. A route whose request or answer holds a `Credential` (`issueServiceCredential`,
271
+ `setModelConfiguration`, and the core's `redeem`, `refresh`, `serviceCredential` and
272
+ `signInCallback`) has no generated command, so no generated command takes a secret in its
273
+ arguments or prints one. The core's own routes (`health`, `signInStart`, `logout` and the
274
+ credential ones) are not a module's methods and have none either.
275
+ - **Flags.** `--selector <json>` (required where the route takes a selector, refused where it takes
276
+ none); `--input <json>`, `--input -` (standard input) or `--input-file <path>`, one of them, a JSON
277
+ object, `{}` when none is given; `--idempotency-key <key>` on an idempotent route, where a key is
278
+ minted and printed on standard error when none is given, and refused on a route that is not
279
+ idempotent. A malformed or missing flag is the client's own refusal (exit 2) naming the flag.
280
+ - **Correlation.** A route whose input schema lists `correlation` as required (`mutate`,
281
+ `mutateBatch`, `executeVerb`, `createBranch`, `merge`, `disposeRef`, `abandonBranch`) gets
282
+ `correlation: { kind: "mint" }` when the input has no `correlation` key, and the command prints
283
+ `correlation: minted by the host` on standard error. A `correlation` the person gave is sent as
284
+ given, whatever it holds, and the host's refusal judges it. The condition is read from the
285
+ schema, never from the route's idempotency.
286
+ - **Help.** `openeng <module> <method> --help` prints the route, its method and path, the module, the
287
+ capability, the admitted token kinds, whether it is idempotent, its selector form and what it
288
+ does, each read from the entry; then `input fields:` and, for a route with a selector,
289
+ `selector keys:`, read from the route's schemas, and `example input:`, the route's generated
290
+ example on one line. Map value fields and the members of a top-level `oneOf` are listed. A field
291
+ is one line, `name: required|optional; <type>`; a `oneOf` whose members each pin the same
292
+ discriminator property to a distinct value shows those values, then one line per member with its
293
+ own fields. Nested object, array and map fields are listed to a depth; beyond that, a line says
294
+ fields are nested deeper.
295
+ `openeng help` prints the global usage and `openeng help <module> <method>` prints that command's help,
296
+ as `--help` does; neither needs a host or a session. A hand-written command with no help text
297
+ prints its own usage line for `--help`.
298
+ - **Streamed routes** (`openeng agents-sdk sendMessageStreamed`, `answerPlanStreamed`) print one JSON line
299
+ per event on standard output, after the run ends, and need a `unix:` host.
300
+ - **Calls.** One host call; two when the session had expired. A streamed command holds one
301
+ connection.
302
+
303
+ ## Flags and environment
304
+
305
+ Every command takes `--json`, which prints one JSON value on standard output in place of text.
306
+ The flags a command reads, named in its usage above, are `--project`, `--pattern`, `--consequence`,
307
+ `--rule-id`, `--name`, `--change-set`, `--revision`, `--from`, `--size`, `--idempotency-key`,
308
+ `--rail-groups`, and, for
309
+ `openeng connector create`, `--system`, `--stack`, `--company`, `--team`, `--owner` and `--label`; a
310
+ command that needs one it was not given refuses before any call (exit 2).
311
+
312
+ | setting | flag | what it is |
313
+ |---|---|---|
314
+ | `OPENENG_HOST` | `--host` | `unix:<absolute socket path>` or an `https` origin. Every other form, `http:` included, is refused before any request (exit 2). The flag wins. |
315
+ | `OPENENG_SIGN_IN_ORIGIN` | `--sign-in-origin` | for a `unix:` host, the network host's `http://127.0.0.1:<port>` or `http://[::1]:<port>` origin. Only the browser visits it; no credential is sent to it. Any non-loopback origin is refused. The flag wins. |
316
+ | `OPENENG_SESSION_STORE` | none | `file` selects the file store. Unset selects the platform's credential store where its tool answers, else the file store. Any other value is refused. |
317
+ | `XDG_CONFIG_HOME` | none | the configuration directory when it is an absolute path, else `~/.config`. |
318
+
319
+ `openeng` reads no other environment variable.
320
+
321
+ `openeng login` writes one waiting line to standard error while it waits for the browser, so under
322
+ `--json` standard output stays one JSON value. A loopback listener the system refuses to open, and a
323
+ session store that cannot be read, written or deleted, each print one line (exit 2) and no stack: the
324
+ listener line asks you to allow local loopback listeners and run `openeng login` again; the store line
325
+ names the store and the operation and ends with the next step, `openeng login` again, or `openeng org attach
326
+ <id>` again where the host had already switched the session. The line never holds the system's own
327
+ error text.
328
+
329
+ `openeng login` against an `https` host uses the same loopback sign-in as a socket host, with the host's
330
+ own origin as the sign-in origin. The sign-in uses PKCE: `openeng` mints a verifier, sends its S256
331
+ challenge with the request, and presents the verifier when it redeems the code, so a code redeems only
332
+ for the client that asked for it.
333
+
334
+ ## Output and exit codes
335
+
336
+ Text goes to standard output on success and to standard error on every failure, with one
337
+ exception: the violations `openeng types draft validate` answers go to standard output with exit 4. A refusal the host
338
+ answered prints its kind or rule and class, then the refusal catalogue's meaning, then each next
339
+ option on its own line, naming the command for an option's read where `openeng` has one. When the text
340
+ is the refusal class's and not the rule's own, the output says so. With `--json` a refusal prints
341
+ its own fields with `explanation` and `explains` as the SDK attached them. The client's own refusals
342
+ (exit 2) and a transport failure with no answer (exit 7) carry no catalogue entry and print their own
343
+ message. An error that is not a transport failure prints as an internal failure with exit 7, and its stack
344
+ follows on standard error, with `--json` too, so standard output stays one JSON value.
345
+
346
+ A refusal that arrived in an envelope ends with one line `reference: <correlation id>`, the id the
347
+ host stamped on the envelope, and the `--json` form carries it as a `reference` field. A refusal
348
+ the client builds itself, and an envelope that carries no id, print none. A result whose arm is
349
+ `refused` prints the `correlationId` its arm carries on the same line, and the `--json` form adds the
350
+ `reference` field beside the arm's own fields. The id is the envelope's
351
+ and not the `correlationId` that an `unknown-outcome`, `in-flight` or `internal` refusal carries in its
352
+ own fields, which stays in the first line.
353
+
354
+ An `invalid-request` that names a field prints `invalid-request: field <path>`. For a generated
355
+ command the path is resolved against the route's schemas (through a `oneOf` by the discriminator the
356
+ person sent, through a map by any key, and through an array by its indexed item); where it lands on a
357
+ `oneOf`, the text adds the allowed values of that field's discriminator and the command's `--help` line,
358
+ and `--json` adds those values beside
359
+ the refusal. A path that does not resolve prints the field alone.
360
+
361
+ An unknown outcome (exit 5) prints its correlation and the read that settles it. `openeng` never
362
+ repeats the operation. For a non-envelope answer with status 500 to 599, exit 5 says the operation
363
+ may or may not have taken effect. It settles with the minted idempotency key for a generated
364
+ command, or with `check what openeng <words> changes before sending it again` for a hand-written
365
+ command. A non-envelope 5xx answer to the session refresh instead exits 7 with
366
+ `refresh-unanswered`: the command was not sent. A retry with the same idempotency key can
367
+ retrieve a recorded outcome or attempt a call that never reached the host; an earlier outcome
368
+ is not guaranteed to exist.
369
+
370
+ A non-envelope answer with a status from 400 to 499, such as a proxy's or a wrong address's page, prints
371
+ one line naming the status, the reason phrase the host sent with it where there is one, and the
372
+ route, and says the request may not have reached the platform (exit 7), with no stack. Under
373
+ `--json` its `error.kind` is `host-answer-not-envelope`.
374
+
375
+ A connection failure before the request was written prints the host and system code, says the
376
+ request was not sent and nothing changed, and confirms that sending it again is safe (exit 7).
377
+
378
+ A refused result arm prints each conflict after the explanation, naming its property and current
379
+ committed envelope as JSON (`absent` when the property is absent), followed by each violation's
380
+ path and rule. Empty conflict and violation lists add no lines. With `--json`, both lists remain
381
+ in the result as the SDK answered them.
382
+
383
+ A protocol mismatch (exit 7) prints the client's protocol version, the addressed host and its
384
+ protocol version. When the host read this `openeng`'s protocol version, it asks you to install an `openeng`
385
+ built for that host. Otherwise it says the host did not read this `openeng`'s protocol version and names
386
+ the version it read, or `none`. The catalogue explanation also prints. Under `--json`, the refusal
387
+ carries the host's key as `host` and this client's version as `clientVersion`. The host's `versions`
388
+ are unchanged: `versions.client` is the version the host read, or null, and `versions.host` is the
389
+ host's version.
390
+
391
+ | exit code | meaning |
392
+ |---|---|
393
+ | 0 | success |
394
+ | 2 | the client's own refusal: configuration, host form, store, a missing flag |
395
+ | 3 | `unauthenticated`: not signed in, a sign-in that was cancelled, timed out or whose state differed, or a logout with nothing stored |
396
+ | 4 | a platform refusal (`refused`), a route result whose arm is `refused`, or the violations `openeng types draft validate` answers (printed on standard output) |
397
+ | 5 | `unknown-outcome`, from the host or from a transport that lost the answer; also a non-envelope answer with status 500 to 599 |
398
+ | 6 | `invalid-request`, `not-found` or `bound-exceeded` |
399
+ | 7 | `internal`, `in-flight`, `protocol-mismatch`, a transport failure with no answer, a non-envelope answer with status 400 to 499, or an internal failure of the client |
400
+
401
+ ## Stores
402
+
403
+ The session of one host is stored under that host's key: `unix:` and the canonical absolute socket
404
+ path for a socket host, the origin for an `https` host. A read never falls back to another host's
405
+ entry. The stores:
406
+
407
+ - macOS: the keychain, through `security -i` with the command on standard input.
408
+ - Linux: `secret-tool store`, the secret on standard input.
409
+ - Otherwise, or with `OPENENG_SESSION_STORE=file`: one file per host in
410
+ `$XDG_CONFIG_HOME/openeng/sessions/` (else `~/.config/openeng/sessions/`), mode `0600` in a directory of
411
+ mode `0700`.
412
+
413
+ `openeng whoami` names the store in use.