sentinelayer-cli 0.32.0 → 0.32.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 CHANGED
@@ -929,6 +929,18 @@ Ledger contract:
929
929
  - network access to Sentinelayer API/web
930
930
  - optional: GitHub CLI (`gh`) authenticated for secret injection
931
931
 
932
+ ## Operator and architecture references
933
+
934
+ - First-run engineering onboarding: `docs/ENGINEERING_ONBOARDING.md`
935
+ - Senti session operations, coordination, kill, and rollback conventions: `docs/sessions.md`
936
+ - Release workflow map and rollback drill ownership: `docs/workflow-matrix.md`
937
+ - Release rollback execution: `.github/workflows/rollback.yml` and `.github/scripts/release-rollback-readiness.sh`
938
+ - Architecture and decision records for major surfaces:
939
+ - Hosted MCP connector boundary: `docs/mcp-hosted-connector.md`
940
+ - MCP local/runtime entry points: `docs/mcp.md`
941
+ - Investor-DD architecture: `docs/INVESTOR_DD_ARCHITECTURE.md`
942
+ - CI/CD acknowledged release risks: `.github/policies/cicd-acknowledged-risks.md`
943
+
932
944
  ## Release to npm
933
945
 
934
946
  This repo includes `.github/workflows/release.yml`.
@@ -949,14 +961,15 @@ Release flow:
949
961
 
950
962
  1. Merge to `main` and let `Release Please` open/update the release PR.
951
963
  2. Merge the release PR after Omar, Quality Gates, and Attestation are green.
952
- 3. From a clean checkout of the release commit, run the guarded release command:
964
+ 3. Wait for the main-branch `Release Please` workflow run on that merge commit to complete successfully.
965
+ 4. From a clean checkout of the release commit, run the guarded release command:
953
966
  ```bash
954
967
  git fetch origin main --tags
955
968
  git checkout main && git pull --ff-only
956
969
  npm run release:publish -- --tag v0.1.1 --notes-file release-notes.md
957
970
  ```
958
- 4. The guarded command creates the tag with `git tag -s`, verifies it with `git tag -v`, pushes it, confirms the remote GitHub tag object is annotated and cryptographically verified by an allowlisted signer, then creates the GitHub release with `gh release create --verify-tag`.
959
- 5. The tag push triggers `Release`, which re-verifies upstream checks, release artifact attestations, rollback readiness, protected `package-release` approval, and npm trusted publishing OIDC before publishing.
971
+ 5. The guarded command confirms the exact release commit has one successful main `Release Please` run, creates the tag with `git tag -s`, verifies it with `git tag -v`, pushes it, confirms the remote GitHub tag object is annotated and cryptographically verified by an allowlisted signer, then creates the GitHub release with `gh release create --verify-tag`.
972
+ 6. The tag push triggers `Release`, which re-verifies upstream checks, release artifact attestations, rollback readiness, protected `package-release` approval, and npm trusted publishing OIDC before publishing.
960
973
 
961
974
  Do not run `gh release create v0.1.1` before pushing the signed tag. GitHub release creation creates a lightweight tag when the tag does not already exist, and the Release workflow intentionally fails closed for lightweight tags. If a remote tag already exists as a lightweight tag, `npm run release:publish` refuses to create the GitHub release.
962
975
 
@@ -964,6 +977,7 @@ Release publish now enforces tarball checksum-manifest validation and attestatio
964
977
 
965
978
  Release guardrails now require successful upstream checks on the target commit:
966
979
 
980
+ - `Release Please`
967
981
  - `Quality Summary`
968
982
  - `Omar Gate`
969
983
  - `Attestation Summary`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sentinelayer-cli",
3
- "version": "0.32.0",
3
+ "version": "0.32.2",
4
4
  "description": "Scaffold Sentinelayer spec/prompt/guide artifacts with secure browser auth and token bootstrap.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -13,6 +13,14 @@ import {
13
13
  } from "../config/service.js";
14
14
 
15
15
  const SCOPES = "global|project|env|resolved";
16
+ const REDACTION_MARKER = "[REDACTED]";
17
+ const SENSITIVE_TOP_LEVEL_KEYS = new Set([
18
+ "sentinelayerToken",
19
+ "openaiApiKey",
20
+ "anthropicApiKey",
21
+ "googleApiKey",
22
+ ]);
23
+ const SENSITIVE_KEY_RE = /(authorization|cookie|set-cookie|x-api-key|api[_-]?key|secret|password|credential|token|webhook[_-]?url)/i;
16
24
 
17
25
  function shouldEmitJson(options, command) {
18
26
  const local = Boolean(options && options.json);
@@ -21,14 +29,59 @@ function shouldEmitJson(options, command) {
21
29
  return local || globalFromCommand;
22
30
  }
23
31
 
24
- function displayValue(value) {
32
+ function isPlainObject(value) {
33
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
34
+ }
35
+
36
+ function isSensitiveConfigPath(pathParts = []) {
37
+ const lastKey = String(pathParts[pathParts.length - 1] || "");
38
+ if (!lastKey) return false;
39
+ if (pathParts.length === 1 && SENSITIVE_TOP_LEVEL_KEYS.has(lastKey)) return true;
40
+ if (SENSITIVE_KEY_RE.test(lastKey)) return true;
41
+ if (pathParts.includes("alerts") && lastKey.replace(/[_-]/g, "").toLowerCase() === "url") {
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+
47
+ function redactConfigValue(value, pathParts = [], depth = 0) {
48
+ if (value === undefined) return undefined;
49
+ if (isSensitiveConfigPath(pathParts)) return REDACTION_MARKER;
50
+ if (depth > 8) return REDACTION_MARKER;
51
+ if (Array.isArray(value)) {
52
+ return value.map((entry) => redactConfigValue(entry, pathParts, depth + 1));
53
+ }
54
+ if (isPlainObject(value)) {
55
+ return Object.fromEntries(
56
+ Object.entries(value).map(([key, inner]) => [
57
+ key,
58
+ redactConfigValue(inner, [...pathParts, key], depth + 1),
59
+ ])
60
+ );
61
+ }
62
+ return value;
63
+ }
64
+
65
+ function redactConfigLayer(layer = {}) {
66
+ return redactConfigValue(layer, []);
67
+ }
68
+
69
+ function redactConfigResult(result = {}) {
70
+ return {
71
+ ...result,
72
+ value: redactConfigValue(result.value, [result.key]),
73
+ };
74
+ }
75
+
76
+ function displayValue(key, value) {
77
+ const safeValue = redactConfigValue(value, [key]);
25
78
  if (value === undefined) {
26
79
  return "";
27
80
  }
28
- if (typeof value === "string") {
29
- return value;
81
+ if (typeof safeValue === "string") {
82
+ return safeValue;
30
83
  }
31
- return JSON.stringify(value);
84
+ return JSON.stringify(safeValue);
32
85
  }
33
86
 
34
87
  function assertKnownKey(key) {
@@ -61,7 +114,7 @@ export function registerConfigCommand(program) {
61
114
  JSON.stringify(
62
115
  {
63
116
  scope,
64
- config: layer,
117
+ config: redactConfigLayer(layer),
65
118
  paths: payload.paths,
66
119
  },
67
120
  null,
@@ -78,7 +131,7 @@ export function registerConfigCommand(program) {
78
131
  return;
79
132
  }
80
133
  for (const [key, value] of entries) {
81
- console.log(`${key}: ${displayValue(value)}`);
134
+ console.log(`${key}: ${displayValue(key, value)}`);
82
135
  }
83
136
  });
84
137
 
@@ -103,7 +156,7 @@ export function registerConfigCommand(program) {
103
156
  {
104
157
  key: normalizedKey,
105
158
  scope,
106
- value,
159
+ value: redactConfigValue(value, [normalizedKey]),
107
160
  source,
108
161
  },
109
162
  null,
@@ -119,7 +172,7 @@ export function registerConfigCommand(program) {
119
172
  return;
120
173
  }
121
174
 
122
- console.log(displayValue(value));
175
+ console.log(displayValue(normalizedKey, value));
123
176
  });
124
177
 
125
178
  config
@@ -137,13 +190,13 @@ export function registerConfigCommand(program) {
137
190
  });
138
191
 
139
192
  if (shouldEmitJson(options, command)) {
140
- console.log(JSON.stringify(result, null, 2));
193
+ console.log(JSON.stringify(redactConfigResult(result), null, 2));
141
194
  return;
142
195
  }
143
196
 
144
197
  console.log(pc.green(`Updated ${result.key} in ${result.scope} config.`));
145
198
  console.log(pc.gray(`Path: ${result.path}`));
146
- console.log(`${result.key}: ${displayValue(result.value)}`);
199
+ console.log(`${result.key}: ${displayValue(result.key, result.value)}`);
147
200
  });
148
201
 
149
202
  config