@nodii/dev 0.5.0 → 0.6.2
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/README.md +110 -1
- package/dist/api.js +20 -4
- package/dist/cli.js +33 -2
- package/dist/core.js +24 -6
- package/dist/devops-openapi.json +1 -1
- package/dist/docs.js +204 -0
- package/dist/guidelines.js +223 -0
- package/dist/harness.js +343 -0
- package/dist/upload.js +291 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -36,6 +36,11 @@ refuses those flags. The first command you run after upgrading deletes the old
|
|
|
36
36
|
config and its stored secret (Keychain item or file) without printing it, then
|
|
37
37
|
asks you to run `nodii-dev login`.
|
|
38
38
|
|
|
39
|
+
`nodii-dev` is only the Dev control CLI. The local-stack CLI is `./nodii-stack`,
|
|
40
|
+
run from the `nodii-local-stack` checkout (renamed per operator ruling G3-6,
|
|
41
|
+
with no alias under the old name). Typing one of its verbs here, such as
|
|
42
|
+
`nodii-dev doctor`, fails and names the `./nodii-stack` command instead.
|
|
43
|
+
|
|
39
44
|
Every command accepts `--json`; without it, output is formatted for humans.
|
|
40
45
|
Dev failures print Dev's error code to stderr and exit non-zero.
|
|
41
46
|
|
|
@@ -93,7 +98,34 @@ nodii-dev heartbeat audit [SERVICE]
|
|
|
93
98
|
nodii-dev design entries [--kind KIND] [--group GROUP] [--q TEXT]
|
|
94
99
|
nodii-dev design entry KEY
|
|
95
100
|
nodii-dev design guide PAGE_TYPE
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Design-system guidelines
|
|
96
104
|
|
|
105
|
+
Edit an entry's guideline through Dev, not with signed scripts. A proposal is
|
|
106
|
+
a guideline change set; a person approves or rejects it in Dev (nobody decides
|
|
107
|
+
their own), and the approved edit becomes a design-system PR.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
nodii-dev design guideline list [--drafted] [--kind KIND] [--q TEXT] [--all]
|
|
111
|
+
nodii-dev design guideline show ENTRY > ENTRY.md # the editable Markdown form
|
|
112
|
+
nodii-dev design guideline propose ENTRY --file ENTRY.md [--summary TEXT] [--dry-run]
|
|
113
|
+
nodii-dev design changeset list [--entry KEY] [--state in_review] [--limit N]
|
|
114
|
+
nodii-dev design changeset show GCS-001
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The file has one `## <list>` section per statement list (`whenToUse`,
|
|
118
|
+
`whenNotToUse`, `dos`, `donts`, and for an active guideline `anatomy`,
|
|
119
|
+
`states.states`, `states.loading`, `states.empty`, `states.error`, `copy`,
|
|
120
|
+
`composition`, `data.notes`), one `- ` bullet per item, and `(drafted)` after
|
|
121
|
+
the list name when the text still awaits a real source. A line that is an
|
|
122
|
+
HTML comment is ignored (`<!--` inside an item stays in its text), and a line
|
|
123
|
+
break inside an item reads back as a space. The `#` title, as `show` prints it,
|
|
124
|
+
names the entry: `propose` refuses a file titled for another one. `propose` sends
|
|
125
|
+
only the lists that differ from Dev's current guideline, and `--dry-run`
|
|
126
|
+
prints them without creating anything.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
97
129
|
nodii-dev figma lock FILE_KEY --title TEXT
|
|
98
130
|
nodii-dev figma check FILE_KEY
|
|
99
131
|
nodii-dev figma log-write CHANGE_SET --tool TOOL --nodes NODE[,NODE]
|
|
@@ -137,8 +169,83 @@ nodii-dev people refresh-names
|
|
|
137
169
|
nodii-dev setup # preview files that would change
|
|
138
170
|
nodii-dev setup --yes # merge and write the harness
|
|
139
171
|
nodii-dev setup --check # exit non-zero when it has drifted
|
|
172
|
+
|
|
173
|
+
nodii-dev rulebook list [--section rulebook|services] [--kind KIND] [--q TEXT]
|
|
174
|
+
nodii-dev rulebook show CODE [--version V] # the current text, from Dev
|
|
175
|
+
nodii-dev rulebook diff CODE [--from V] [--to V]
|
|
176
|
+
|
|
177
|
+
nodii-dev docs check PATH... # local M-05 checks only
|
|
178
|
+
nodii-dev docs validate PATH... --class CLASS # Dev's dry run of the upload
|
|
179
|
+
nodii-dev docs upload PATH... --class ruling|conformance|editorial
|
|
180
|
+
[--base CODE=VERSION ...] [--decision DECISION.json]
|
|
181
|
+
[--conformance-ref PR|REPO@SHA] [--meaning-unchanged REASON]
|
|
182
|
+
[--wait [--wait-minutes N]]
|
|
183
|
+
nodii-dev docs ratify --domain KEY [--domain KEY ...] --decision DECISION.json [--wait]
|
|
184
|
+
nodii-dev docs parked [CODE] [--status pending|acknowledged]
|
|
185
|
+
|
|
186
|
+
nodii-dev harness install [--include-drafts] [--adopt PATH,...] [--dry-run]
|
|
187
|
+
nodii-dev harness update [--include-drafts] [--adopt PATH,...] [--dry-run]
|
|
188
|
+
nodii-dev harness update --check # exit 1 when Dev would write anything different
|
|
140
189
|
```
|
|
141
190
|
|
|
191
|
+
## AD documents
|
|
192
|
+
|
|
193
|
+
AD documents (the rulebook and per-service feature docs) live in Dev, one
|
|
194
|
+
current text per document with its version history (M-05). `docs upload`
|
|
195
|
+
takes files or folders (folders are searched for `.md` files, skipping `_`
|
|
196
|
+
and `.` entries) and checks every document locally before sending anything:
|
|
197
|
+
front matter, the section order for its kind, unique clause ids that use the
|
|
198
|
+
document's code, no bare decision ids (`D123`; cite `pre:D123`), and the
|
|
199
|
+
parked (`Undecided`) items, which it lists. All problems are printed with
|
|
200
|
+
Dev's error codes, and nothing is sent until there are none. Dev then runs the
|
|
201
|
+
same checks, plus the ones that need its data, in one transaction.
|
|
202
|
+
|
|
203
|
+
- `--class` is required on every write (M05-8). A `ruling` carries its
|
|
204
|
+
decision (`--decision`, with `amends`), and the decision locks with the new
|
|
205
|
+
versions or nothing is written. An `editorial` write that changes a Rules
|
|
206
|
+
clause is refused unless `--meaning-unchanged REASON` is given (M05-10).
|
|
207
|
+
- A new document is based on nothing. For a document Dev already has, pass
|
|
208
|
+
the version you started from with `--base CODE=VERSION` (M05-11). The CLI
|
|
209
|
+
never fills it in from Dev, so a stale edit is refused instead of
|
|
210
|
+
overwriting newer text.
|
|
211
|
+
- **Parked items:** when a document has parked items the operator has not
|
|
212
|
+
acknowledged, Dev refuses the upload and records the list. The CLI prints
|
|
213
|
+
it and exits `4`. The operator acknowledges it in Dev (this CLI is an OAuth
|
|
214
|
+
app and cannot). Re-run the upload then, or pass `--wait` to poll and retry
|
|
215
|
+
once it is acknowledged.
|
|
216
|
+
- `docs ratify` is the bulk ratification mode: one decision locks v1.0 of
|
|
217
|
+
every draft document in the named domains, which then become AD (M05-2).
|
|
218
|
+
Upload the drafts first (`--class conformance`), then ratify domain by
|
|
219
|
+
domain. The decision file holds `title`, `context`, `options`, `ruling`,
|
|
220
|
+
`consequences` and `provenance`; Dev mints the `DEC-nnnn` id and generates
|
|
221
|
+
the amends. A domain that is already AD can be named again to ratify drafts
|
|
222
|
+
added to it since (a new feature doc in a ratified service).
|
|
223
|
+
- `docs validate` also prints Dev's warnings for each document (they never
|
|
224
|
+
block) and any problems with the decision's amends.
|
|
225
|
+
|
|
226
|
+
Like every other command, these reach Dev only through the platform edge
|
|
227
|
+
(`/devops/api/v1/documents`, `/domains`, `/harness`, …) with the OAuth login.
|
|
228
|
+
|
|
229
|
+
## Agent harness
|
|
230
|
+
|
|
231
|
+
`harness install` writes the repository's agent harness as Dev generates it
|
|
232
|
+
from the ratified methodology: the `CLAUDE.md` and `AGENTS.md` pointer
|
|
233
|
+
blocks, one pointer skill per methodology document, the Claude Code hooks
|
|
234
|
+
merged into `.claude/settings.json`, and Dev's `.nodii/harness.json`. Every
|
|
235
|
+
generated file carries the methodology version it came from. Nothing about
|
|
236
|
+
the rules is authored locally. Run it in the service's own repository: it
|
|
237
|
+
installs one service at a time, and there is no fleet mode. `--include-drafts`
|
|
238
|
+
uses draft methodology while none is ratified.
|
|
239
|
+
|
|
240
|
+
The CLI records what it wrote in `.nodii/harness-state.json` and never touches
|
|
241
|
+
anything else. An existing file it did not write is refused, unless the
|
|
242
|
+
operator names it with `--adopt PATH` (a `CLAUDE.md` then keeps its text and
|
|
243
|
+
gains the managed block; `settings.json` keeps its other keys and hooks).
|
|
244
|
+
`harness update` re-syncs: it prints the methodology version change, which
|
|
245
|
+
source documents moved, and each file created, updated or removed. It
|
|
246
|
+
removes only files, blocks and hook entries it wrote, and it refuses (writing
|
|
247
|
+
nothing) when one of its files was edited by hand.
|
|
248
|
+
|
|
142
249
|
## Build lanes and repository setup
|
|
143
250
|
|
|
144
251
|
`lane start` creates and starts a lane. It infers the repository, branch and
|
|
@@ -190,7 +297,9 @@ runtime, people, agents, inbox/gates, design, runbooks,
|
|
|
190
297
|
doctrine and exceptions, changelog, health verdicts, approval kinds,
|
|
191
298
|
notification registry, skills, questions, decisions, drift, requests,
|
|
192
299
|
pipelines, runners, routing, usage, audit, reconciliation, cron, composite
|
|
193
|
-
surfaces, service edges, lanes, swaps, ideas,
|
|
300
|
+
surfaces, service edges, lanes, swaps, ideas, terminology, and the AD era: `rulebook`
|
|
301
|
+
(documents, plus `rulebook versions CODE`), `ad-decisions`, `domains`, `standardizations`,
|
|
302
|
+
`archive` (`archive resolve --ref pre:D1366`), and `harness methodology`. `api list` is the authoritative discovery view;
|
|
194
303
|
`api OPERATION_ID` makes every operation callable even without an alias.
|
|
195
304
|
|
|
196
305
|
`figma check` exits `0` when allowed and `3` when denied. The pre-hook permits
|
package/dist/api.js
CHANGED
|
@@ -95,12 +95,14 @@ export const GROUPS = {
|
|
|
95
95
|
sagas: "Sagas", "outbox-topics": ["Outbox", "OutboxTopics"], tables: "Tables", libraries: "Libraries", workers: "Workers",
|
|
96
96
|
projects: "Projects", issues: "Issues", backlog: "Backlogs", backlogs: "Backlogs", builds: "Delivery", changes: "Delivery",
|
|
97
97
|
runtime: "Runtime", people: "People", agents: "Agents", inbox: "Inbox", gate: "Inbox", design: "Design", runbooks: "Runbooks",
|
|
98
|
-
doctrine: ["Doctrine", "DoctrineExceptions"],
|
|
98
|
+
doctrine: ["Doctrine", "DoctrineExceptions"], changelog: "Changelog", "health-verdicts": "HealthVerdicts",
|
|
99
99
|
"approval-kinds": "ApprovalKinds", "notification-registry": "Notification registry", skills: "Skills", "open-questions": "OpenQuestions",
|
|
100
100
|
decisions: "Decisions", drift: "Drift", requests: "Requests", pipelines: "Pipelines", slices: "Slices", "sql-lookups": "SqlLookups",
|
|
101
101
|
runners: "Runners", routing: "Routing", usage: "Usage", audit: "AuditLog", reconciliation: "Reconciliation", cron: "Cron", health: "Health",
|
|
102
102
|
composite: "Composite", "service-edges": "ServiceEdges", lanes: "Lanes",
|
|
103
|
-
swaps: "Swaps", ideas: "Ideas", terminology: "Terminology",
|
|
103
|
+
swaps: "Swaps", ideas: "Ideas", terminology: "Terminology", credentials: "Credentials",
|
|
104
|
+
rulebook: "ad-documents", "ad-decisions": "ad-decisions", domains: "ad-domains", standardizations: "standardizations",
|
|
105
|
+
archive: "archive", harness: "harness",
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
export const ALIASES = {
|
|
@@ -129,7 +131,7 @@ export const ALIASES = {
|
|
|
129
131
|
"gate:fleet": "getFleetGate", "gate:service": "getServiceGate", "gate:dry-run": "getGateDryRun",
|
|
130
132
|
"runbooks:list": "listRunbooks", "runbooks:show": "getRunbook", "runbooks:create": "registerRunbook", "runbooks:update": "updateRunbook",
|
|
131
133
|
"doctrine:list": "listDoctrine", "doctrine:show": "getDoctrine", "doctrine:create": "createDoctrine", "doctrine:revise": "reviseDoctrine",
|
|
132
|
-
"
|
|
134
|
+
"changelog:list": "listChangelog", "changelog:append": "appendChangelog",
|
|
133
135
|
"health-verdicts:list": "listHealthVerdicts", "health-verdicts:post": "postHealthVerdict",
|
|
134
136
|
"approval-kinds:list": "listApprovalKinds", "approval-kinds:show": "getApprovalKind", "approval-kinds:create": "createApprovalKind",
|
|
135
137
|
"notification-registry:list": "listNotificationEvents", "notification-registry:show": "getNotificationEvent", "notification-registry:propose": "proposeNotificationEvent",
|
|
@@ -143,14 +145,28 @@ export const ALIASES = {
|
|
|
143
145
|
"service-edges:list": "listServiceEdges", "service-edges:show": "getServiceEdgesForService",
|
|
144
146
|
"swaps:list": "listSwaps", "swaps:show": "getSwap", "swaps:create": "createSwap", "swaps:close": "closeSwap",
|
|
145
147
|
"ideas:list": "listIdeas", "ideas:show": "getIdea", "ideas:create": "createIdea", "ideas:status": "updateIdeaStatus",
|
|
148
|
+
"rulebook:versions": "listAdDocumentVersions", "rulebook:parked": "getAdDocumentParked",
|
|
149
|
+
"ad-decisions:list": "listAdDecisions", "ad-decisions:show": "getAdDecision", "ad-decisions:lock": "lockAndAmendAdDecision",
|
|
150
|
+
"domains:list": "listAdDomains", "domains:show": "getAdDomain",
|
|
151
|
+
"standardizations:list": "listStandardizations", "standardizations:show": "getStandardization", "standardizations:update": "updateStandardization",
|
|
152
|
+
"archive:search": "searchPreDevArchive", "archive:resolve": "resolvePreCitation",
|
|
153
|
+
"harness:show": "generateHarness", "harness:methodology": "getHarnessMethodologyVersion",
|
|
146
154
|
"terminology:list": "listTerminology", "terminology:show": "getTerminologyTerm", "terminology:create": "createTerminologyTerm", "terminology:update": "updateTerminologyTerm",
|
|
147
155
|
};
|
|
148
156
|
|
|
157
|
+
// Decisions a person makes in Dev (operator authority): nodii-dev never sends them.
|
|
158
|
+
export const PERSON_ONLY = new Set(["approveDesignGuidelineChangeSet", "rejectDesignGuidelineChangeSet", "mergeDesignRelease"]);
|
|
159
|
+
export function refusePersonOnly(operationId) {
|
|
160
|
+
if (PERSON_ONLY.has(operationId))
|
|
161
|
+
throw new CliError("PERSON_ONLY", `${operationId} is a person's decision in Dev; nodii-dev does not send it.`, 2);
|
|
162
|
+
}
|
|
163
|
+
|
|
149
164
|
export function findFirstClass(operations, group, action) {
|
|
150
165
|
const aliased = ALIASES[`${group}:${action}`];
|
|
151
166
|
if (aliased) return operations.find(x => x.operationId === aliased);
|
|
152
167
|
const tags = Array.isArray(GROUPS[group]) ? GROUPS[group] : [GROUPS[group]];
|
|
153
|
-
|
|
168
|
+
// A person-only operation is never picked by name, so a guessed verb cannot reach it.
|
|
169
|
+
const candidates = operations.filter(x => !PERSON_ONLY.has(x.operationId) && x.tags.some(t => tags.includes(t)));
|
|
154
170
|
const normalized = action?.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).toLowerCase();
|
|
155
171
|
return candidates.find(x => x.operationId.toLowerCase() === normalized || x.operationId.toLowerCase().endsWith(normalized));
|
|
156
172
|
}
|
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
} from "./oauth.js";
|
|
25
25
|
import { heartbeatCurrentLane, postHook, preHook, settingsSnippet, workflowSuggestion } from "./hooks.js";
|
|
26
26
|
import { setupCommand } from "./setup.js";
|
|
27
|
+
import { harnessCommand } from "./harness.js";
|
|
28
|
+
import { docsCommand, rulebookCommand } from "./upload.js";
|
|
29
|
+
import { changeSetCommand, guidelineCommand } from "./guidelines.js";
|
|
27
30
|
import {
|
|
28
31
|
ALIASES,
|
|
29
32
|
GROUPS,
|
|
@@ -31,6 +34,7 @@ import {
|
|
|
31
34
|
callOperation,
|
|
32
35
|
findFirstClass,
|
|
33
36
|
flagsToOperation,
|
|
37
|
+
refusePersonOnly,
|
|
34
38
|
loadOpenApi,
|
|
35
39
|
operationsFrom,
|
|
36
40
|
parseParams,
|
|
@@ -56,9 +60,20 @@ Usage: nodii-dev <command> [options] [--json]
|
|
|
56
60
|
drift file | request file
|
|
57
61
|
decisions list|get|search
|
|
58
62
|
design entries|entry|guide
|
|
63
|
+
design guideline list [--drafted] [--all] | show ENTRY
|
|
64
|
+
design guideline propose ENTRY --file FILE.md [--summary TEXT] [--dry-run]
|
|
65
|
+
design changeset list [--entry KEY] [--state STATE] | show ID
|
|
59
66
|
figma lock|check|log-write|submit|apply|published|abandon|release
|
|
60
67
|
hook claude-figma-pre|claude-figma-post|print-settings
|
|
61
68
|
setup [--yes|--check] # dry-run by default
|
|
69
|
+
harness install|update [--include-drafts[=false]] [--adopt PATH,...] [--dry-run]
|
|
70
|
+
harness update --check # exit 1 when Dev would write anything different
|
|
71
|
+
docs check|validate|upload <path...> --class ruling|conformance|editorial
|
|
72
|
+
[--base CODE=VERSION] [--decision FILE] [--conformance-ref REF]
|
|
73
|
+
[--meaning-unchanged REASON] [--wait]
|
|
74
|
+
docs ratify --domain KEY [--domain KEY ...] --decision FILE [--wait]
|
|
75
|
+
docs parked [CODE] [--status pending|acknowledged]
|
|
76
|
+
rulebook list|show CODE [--version V]|diff CODE
|
|
62
77
|
|
|
63
78
|
lane start <key> --title TEXT --kind KIND --side SIDE [--repo REPO] [--issue ISSUE]
|
|
64
79
|
lane step <key> --side SIDE --start|--done|--block <step>
|
|
@@ -79,10 +94,15 @@ Resource commands cover every OpenAPI tag, including services, feature-docs,
|
|
|
79
94
|
contracts, sagas, outbox-topics, tables, libraries, workers, projects, issues,
|
|
80
95
|
backlogs, builds, changes, deploy-reports, runtime, people, agents, inbox,
|
|
81
96
|
gate, design, runbooks, doctrine, changelog, health-verdicts, approval-kinds,
|
|
82
|
-
notification-registry, skills, open-questions, decisions, drift,
|
|
97
|
+
notification-registry, skills, open-questions, decisions, drift, requests,
|
|
98
|
+
rulebook, ad-decisions, domains, standardizations, archive, and harness.
|
|
83
99
|
|
|
84
100
|
Run a command with --help or see the package README for all flags.`;
|
|
85
101
|
|
|
102
|
+
// Verbs of the local-stack CLI, which shared this name until it became
|
|
103
|
+
// ./nodii-stack (operator G3-6, no alias). Named here only to point there.
|
|
104
|
+
const STACK_VERBS = new Set(["up", "down", "doctor", "sync", "seed", "wipe", "reset", "rebuild", "restart", "smoke", "urls", "peek", "trace", "stats", "bootstrap", "provenance", "stale", "status", "logs", "world", "totp", "token", "bench"]);
|
|
105
|
+
|
|
86
106
|
async function stdinText() {
|
|
87
107
|
let value = "";
|
|
88
108
|
for await (const chunk of stdin) value += chunk;
|
|
@@ -222,6 +242,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
222
242
|
if (!action) throw new CliError("USAGE", "api needs an operationId or list", 2);
|
|
223
243
|
const entry = operations.find((x) => x.operationId === action);
|
|
224
244
|
if (!entry) throw new CliError("UNKNOWN_OPERATION", `No OpenAPI operationId: ${action}`, 2);
|
|
245
|
+
refusePersonOnly(entry.operationId);
|
|
225
246
|
const parsed = await readBody(f.body);
|
|
226
247
|
const request = buildOperationRequest(entry, parseParams(f.param), parsed?.value);
|
|
227
248
|
return emit(await devRawRequest(request.method, request.path, parsed?.text), json);
|
|
@@ -248,6 +269,16 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
248
269
|
}
|
|
249
270
|
const emitCommand = async (result) => emit(result.payload, json, result.human);
|
|
250
271
|
if (command === "setup") return emitCommand(await setupCommand(f));
|
|
272
|
+
if (command === "harness" && ["install", "update"].includes(action)) {
|
|
273
|
+
// `--include-drafts` is a switch: `--include-drafts false` would leave `false` here and turn drafts on.
|
|
274
|
+
if (rest.length) throw new CliError("USAGE", `harness ${action} takes no arguments, got: ${rest.join(" ")}. Write a flag value as --include-drafts=false.`, 2);
|
|
275
|
+
return emitCommand(await harnessCommand(action, f));
|
|
276
|
+
}
|
|
277
|
+
if (command === "docs") return emitCommand(await docsCommand(action, rest, f));
|
|
278
|
+
if (command === "rulebook" && ["list", "show", "diff"].includes(action)) return emitCommand(await rulebookCommand(action, rest, f));
|
|
279
|
+
// Guideline change sets: approving or rejecting one is a person's action in Dev.
|
|
280
|
+
if (command === "design" && action === "guideline") return emitCommand(await guidelineCommand(rest[0], rest.slice(1), f));
|
|
281
|
+
if (command === "design" && action === "changeset") return emitCommand(await changeSetCommand(rest[0], rest.slice(1), f));
|
|
251
282
|
if (command === "lane") return emitCommand(await laneCommand(action, rest, f));
|
|
252
283
|
if (command === "next") return emitCommand(await nextCommand(action, f));
|
|
253
284
|
if (command === "built") return emitCommand(await builtCommand(action, rest, f));
|
|
@@ -568,7 +599,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
568
599
|
}
|
|
569
600
|
throw new CliError(
|
|
570
601
|
"USAGE",
|
|
571
|
-
`Unknown command: ${[command, action].filter(Boolean).join(" ")}`,
|
|
602
|
+
`Unknown command: ${[command, action].filter(Boolean).join(" ")}${STACK_VERBS.has(command) ? `. The local-stack CLI is \`./nodii-stack ${command}\`, run from the nodii-local-stack checkout.` : ""}`,
|
|
572
603
|
2,
|
|
573
604
|
);
|
|
574
605
|
}
|
package/dist/core.js
CHANGED
|
@@ -4,7 +4,7 @@ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { homedir, platform } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
|
|
7
|
-
export const VERSION = "0.
|
|
7
|
+
export const VERSION = "0.6.2";
|
|
8
8
|
export const configPath = () =>
|
|
9
9
|
process.env.NODII_DEV_CONFIG_PATH ??
|
|
10
10
|
join(homedir(), ".config/nodii-dev/config.json");
|
|
@@ -105,10 +105,10 @@ export function requestUrl(config, path) {
|
|
|
105
105
|
return `${config.edgeUrl.replace(/\/$/, "")}/devops${path}`;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
async function devFetch(method, path, bodyText, contentType, accept) {
|
|
109
109
|
const config = await readConfig();
|
|
110
110
|
const auth = await authProvider();
|
|
111
|
-
const headers = { accept
|
|
111
|
+
const headers = { accept };
|
|
112
112
|
if (bodyText !== undefined) headers["content-type"] = contentType;
|
|
113
113
|
Object.assign(headers, await auth.headers({ method, path, body: bodyText ?? "" }));
|
|
114
114
|
const url = requestUrl(config, path);
|
|
@@ -118,7 +118,11 @@ export async function devRawRequest(method, path, bodyText, contentType = "appli
|
|
|
118
118
|
} catch (error) {
|
|
119
119
|
throw new CliError("DEV_UNREACHABLE", `Cannot reach Dev at ${new URL(url).origin}: ${error.message}`);
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
return { response, raw: await response.text() };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function devRawRequest(method, path, bodyText, contentType = "application/json") {
|
|
125
|
+
const { response, raw } = await devFetch(method, path, bodyText, contentType, "application/json");
|
|
122
126
|
let payload;
|
|
123
127
|
try {
|
|
124
128
|
payload = raw ? JSON.parse(raw) : {};
|
|
@@ -135,6 +139,15 @@ export async function devRawRequest(method, path, bodyText, contentType = "appli
|
|
|
135
139
|
return payload;
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
/** A Dev route that answers with text (e.g. a document body as text/markdown). Errors are Dev's JSON. */
|
|
143
|
+
export async function devTextRequest(method, path) {
|
|
144
|
+
const { response, raw } = await devFetch(method, path, undefined, undefined, "text/markdown, text/plain, application/json");
|
|
145
|
+
if (response.ok) return raw;
|
|
146
|
+
let payload = {};
|
|
147
|
+
try { payload = JSON.parse(raw); } catch { payload = { message: raw }; }
|
|
148
|
+
throw new CliError(payload.code ?? `HTTP_${response.status}`, payload.message ?? `Dev returned HTTP ${response.status}`, 1, payload.details);
|
|
149
|
+
}
|
|
150
|
+
|
|
138
151
|
export const devRequest = (method, path, body) =>
|
|
139
152
|
devRawRequest(method, path, body === undefined ? undefined : JSON.stringify(body));
|
|
140
153
|
|
|
@@ -147,6 +160,9 @@ export async function checkLogin() {
|
|
|
147
160
|
}
|
|
148
161
|
|
|
149
162
|
export const encode = (value) => encodeURIComponent(value);
|
|
163
|
+
// Switches never take the next token as their value, so
|
|
164
|
+
// `docs upload --wait rulebook/` keeps rulebook/ as a path.
|
|
165
|
+
const SWITCHES = new Set(["json", "wait", "dry-run", "check", "all", "fleet", "help", "yes", "mine", "no-browser", "include-drafts", "drafted"]);
|
|
150
166
|
export function parseArgs(argv) {
|
|
151
167
|
const flags = {};
|
|
152
168
|
const args = [];
|
|
@@ -156,11 +172,13 @@ export function parseArgs(argv) {
|
|
|
156
172
|
args.push(token);
|
|
157
173
|
continue;
|
|
158
174
|
}
|
|
159
|
-
const
|
|
175
|
+
const at = token.indexOf("=");
|
|
176
|
+
const key = at === -1 ? token.slice(2) : token.slice(2, at);
|
|
177
|
+
const inline = at === -1 ? undefined : token.slice(at + 1);
|
|
160
178
|
const value =
|
|
161
179
|
inline !== undefined
|
|
162
180
|
? inline
|
|
163
|
-
: argv[i + 1] && !argv[i + 1].startsWith("--")
|
|
181
|
+
: !SWITCHES.has(key) && argv[i + 1] && !argv[i + 1].startsWith("--")
|
|
164
182
|
? argv[++i]
|
|
165
183
|
: true;
|
|
166
184
|
flags[key] =
|