filegrc 0.6.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 +1 -1
- package/package.json +1 -1
- package/src/cli.js +6 -1
- package/src/web.js +14 -30
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ npx filegrc migrate --to-model 2 --preview --json
|
|
|
60
60
|
|
|
61
61
|
Follow the [model v2 upgrade guide](https://github.com/Alignbase/filegrc/blob/main/docs/upgrading-to-model-v2.md), then run the model v3 and v4 previews in order.
|
|
62
62
|
|
|
63
|
-
`filegrc serve --help` prints bind, port, environment, and safety options without starting the server. The editable server
|
|
63
|
+
`filegrc serve --help` prints bind, port, environment, and safety options without starting the server. The editable server prefers `127.0.0.1:8787` and chooses another available port when that port is occupied. Set `FILEGRC_HOST`, `FILEGRC_PORT`, or the matching flags when needed. In trunk mode, browser saves synchronize, commit, and push from the authoritative branch. Use `--allow-non-authoritative-writes` for local development in a task checkout; the override never commits or pushes.
|
|
64
64
|
|
|
65
65
|
`filegrc setup` provides the headless equivalent of browser onboarding. Run it without arguments for guided terminal setup, or pass all initial service-boundary fields and a management program goal as flags or a JSON payload. Add `--preview` to validate and inspect the planned System and Program writes without saving. Add `--summary --json` for compact agent output. Selecting Type 1 or Type 2 updates the Program goal and selected Systems. Setup does not select Framework records, link Controls, create Evidence Artifacts, or create an Audit record.
|
|
66
66
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -96,6 +96,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
96
96
|
const result = await serveWorkspace(positionals[0] ?? root, {
|
|
97
97
|
host: flags.host ?? process.env.FILEGRC_HOST,
|
|
98
98
|
port: flags.port ?? process.env.FILEGRC_PORT,
|
|
99
|
+
fallbackToAvailablePort: true,
|
|
99
100
|
allowNonAuthoritativeWrites: flags["allow-non-authoritative-writes"] === true
|
|
100
101
|
});
|
|
101
102
|
const stopped = new Promise((resolvePromise) => {
|
|
@@ -107,6 +108,9 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
107
108
|
process.once("SIGINT", stop);
|
|
108
109
|
process.once("SIGTERM", stop);
|
|
109
110
|
});
|
|
111
|
+
if (result.usedFallbackPort) {
|
|
112
|
+
console.log(`Port ${result.requestedPort} is already in use. Using ${result.address.port} instead.`);
|
|
113
|
+
}
|
|
110
114
|
console.log(`filegrc workspace: ${result.url}`);
|
|
111
115
|
console.log(`Data: ${result.root}/data`);
|
|
112
116
|
printGithubStarMessage();
|
|
@@ -1048,7 +1052,8 @@ function printCommandHelp(command) {
|
|
|
1048
1052
|
|
|
1049
1053
|
Options:
|
|
1050
1054
|
--host <address> Bind address. Defaults to FILEGRC_HOST or 127.0.0.1.
|
|
1051
|
-
--port <number>
|
|
1055
|
+
--port <number> Preferred port. Defaults to FILEGRC_PORT or 8787. If occupied,
|
|
1056
|
+
the server uses an available port. Use 0 to choose one directly.
|
|
1052
1057
|
--root <path> Workspace path when no positional root is given.
|
|
1053
1058
|
--allow-non-authoritative-writes
|
|
1054
1059
|
Allow local browser writes from a task checkout. This explicit
|
package/src/web.js
CHANGED
|
@@ -482,7 +482,7 @@ function openCollectionReviewDialog(type) {
|
|
|
482
482
|
const dialog = document.createElement("dialog");
|
|
483
483
|
dialog.className = "commit-dialog event-dialog collection-review-dialog";
|
|
484
484
|
dialog.setAttribute("aria-labelledby", "collection-review-dialog-title");
|
|
485
|
-
dialog.innerHTML = '<form><div class="dialog-head"><div><p class="kicker">Scope confirmation</p><h2 id="collection-review-dialog-title">Confirm ' + esc(configuration.title.toLowerCase()) + '</h2></div><button type="button" class="icon-button" aria-label="Close">×</button></div><p>' + esc(configuration.description) + '</p><section class="event-dialog-steps collection-review-checks"><strong>Before confirming</strong><ul>' + configuration.reviewPoints.map((point) => '<li>' + esc(point) + '</li>').join("") + '</ul></section><div class="form-grid"><label><span>Conclusion</span><select name="decision" required>' + decisions + '</select></label><label><span>Reviewer</span><select name="reviewerId" required><option value="">Select</option>' + people.map(({ record }) => '<option value="' + esc(record.id) + '" ' + ((assessment.review?.reviewedByIds || []).includes(record.id) ? "selected" : "") + '>' + esc(record.title) + '</option>').join("") + '</select></label><label><span>Reviewed on</span><input name="reviewedOn" type="date" required value="' + esc(assessment.review?.reviewedOn || currentDate()) + '"></label><label data-authoritative-system><span>Authoritative ' + (v4 ? "Component" : "System") + '</span><select name="authoritativeSourceId"><option value="">Select</option>' + systems.map(({ record }) => '<option value="' + esc(record.id) + '" ' + ((assessment.review?.authoritativeComponentId || assessment.review?.authoritativeSystemId) === record.id ? "selected" : "") + '>' + esc(record.title) + '</option>').join("") + '</select></label><label class="full"><span>Review notes</span><textarea name="rationale" rows="3" required placeholder="Note what you confirmed and any scope decision that needs context.">' + esc(assessment.review?.rationale || "") + '</textarea></label></div><div class="workflow-preview"
|
|
485
|
+
dialog.innerHTML = '<form><div class="dialog-head"><div><p class="kicker">Scope confirmation</p><h2 id="collection-review-dialog-title">Confirm ' + esc(configuration.title.toLowerCase()) + '</h2></div><button type="button" class="icon-button" aria-label="Close">×</button></div><p>' + esc(configuration.description) + '</p><section class="event-dialog-steps collection-review-checks"><strong>Before confirming</strong><ul>' + configuration.reviewPoints.map((point) => '<li>' + esc(point) + '</li>').join("") + '</ul></section><div class="form-grid"><label><span>Conclusion</span><select name="decision" required>' + decisions + '</select></label><label><span>Reviewer</span><select name="reviewerId" required><option value="">Select</option>' + people.map(({ record }) => '<option value="' + esc(record.id) + '" ' + ((assessment.review?.reviewedByIds || []).includes(record.id) ? "selected" : "") + '>' + esc(record.title) + '</option>').join("") + '</select></label><label><span>Reviewed on</span><input name="reviewedOn" type="date" required value="' + esc(assessment.review?.reviewedOn || currentDate()) + '"></label><label data-authoritative-system><span>Authoritative ' + (v4 ? "Component" : "System") + '</span><select name="authoritativeSourceId"><option value="">Select</option>' + systems.map(({ record }) => '<option value="' + esc(record.id) + '" ' + ((assessment.review?.authoritativeComponentId || assessment.review?.authoritativeSystemId) === record.id ? "selected" : "") + '>' + esc(record.title) + '</option>').join("") + '</select></label><label class="full"><span>Review notes</span><textarea name="rationale" rows="3" required placeholder="Note what you confirmed and any scope decision that needs context.">' + esc(assessment.review?.rationale || "") + '</textarea></label></div><div class="workflow-preview"><strong>What this saves</strong><p>Confirm ' + assessment.recordCount + ' current ' + esc(pluralize("record", assessment.recordCount)) + '. If the collection or material scope changes, FileGRC will ask for another review.</p></div><div class="dialog-error" role="alert"></div><div class="dialog-actions"><span class="save-status review-save-status" role="status" aria-live="polite"></span><button type="button" class="button" data-event="cancel">Cancel</button><button type="submit" class="button primary">Confirm and save</button></div></form>';
|
|
486
486
|
document.body.append(dialog);
|
|
487
487
|
dialog.showModal();
|
|
488
488
|
const form = dialog.querySelector("form");
|
|
@@ -498,12 +498,8 @@ function openCollectionReviewDialog(type) {
|
|
|
498
498
|
dialog.querySelector(".icon-button").addEventListener("click", () => dialog.close());
|
|
499
499
|
dialog.querySelector('[data-event="cancel"]').addEventListener("click", () => dialog.close());
|
|
500
500
|
dialog.addEventListener("close", () => dialog.remove());
|
|
501
|
-
let previewedPayload = null;
|
|
502
501
|
form.addEventListener("input", () => {
|
|
503
|
-
previewedPayload = null;
|
|
504
502
|
syncDecision();
|
|
505
|
-
dialog.querySelector(".workflow-preview").innerHTML = "";
|
|
506
|
-
dialog.querySelector("[data-preview-collection-review]").textContent = "Preview confirmation";
|
|
507
503
|
});
|
|
508
504
|
form.addEventListener("submit", async (event) => {
|
|
509
505
|
event.preventDefault();
|
|
@@ -521,32 +517,20 @@ function openCollectionReviewDialog(type) {
|
|
|
521
517
|
};
|
|
522
518
|
form.querySelectorAll("button,input,select,textarea").forEach((control) => { control.disabled = true; });
|
|
523
519
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
saveStatus.textContent = "Validating and saving…";
|
|
537
|
-
const prefetch = await repositoryPrefetch;
|
|
538
|
-
const response = await localFetch("/api/collection-review", {
|
|
539
|
-
method: "POST",
|
|
540
|
-
headers: { "content-type": "application/json", prefer: "respond-async" },
|
|
541
|
-
body: JSON.stringify({ ...previewedPayload, confirmed: true, prefetchToken: prefetch?.token })
|
|
542
|
-
});
|
|
543
|
-
if (!response.ok) throw new Error(await responseMessage(response));
|
|
544
|
-
saveStatus.textContent = "Saved locally. Refreshing page…";
|
|
545
|
-
applyMutationState(await response.json());
|
|
546
|
-
dialog.close();
|
|
547
|
-
render();
|
|
548
|
-
}
|
|
520
|
+
saveStatus.textContent = "Validating and saving…";
|
|
521
|
+
const prefetch = await repositoryPrefetch;
|
|
522
|
+
const response = await localFetch("/api/collection-review", {
|
|
523
|
+
method: "POST",
|
|
524
|
+
headers: { "content-type": "application/json", prefer: "respond-async" },
|
|
525
|
+
body: JSON.stringify({ ...payload, confirmed: true, prefetchToken: prefetch?.token })
|
|
526
|
+
});
|
|
527
|
+
if (!response.ok) throw new Error(await responseMessage(response));
|
|
528
|
+
saveStatus.textContent = "Saved locally. Refreshing page…";
|
|
529
|
+
applyMutationState(await response.json());
|
|
530
|
+
dialog.close();
|
|
531
|
+
render();
|
|
549
532
|
} catch (requestError) {
|
|
533
|
+
saveStatus.textContent = "Not saved";
|
|
550
534
|
error.textContent = requestError.message;
|
|
551
535
|
} finally {
|
|
552
536
|
form.querySelectorAll("button,input,select,textarea").forEach((control) => { control.disabled = false; });
|