sentinelayer-cli 0.18.2 → 0.20.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sentinelayer-cli",
3
- "version": "0.18.2",
3
+ "version": "0.20.0",
4
4
  "description": "Scaffold Sentinelayer spec/prompt/guide artifacts with secure browser auth and token bootstrap.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -39,6 +39,16 @@ import {
39
39
  writeArtifact,
40
40
  } from "./shared.js";
41
41
 
42
+ function maskEmailForDisplay(value) {
43
+ const email = String(value || "").trim();
44
+ if (!email) return "unknown-email";
45
+ const atIndex = email.indexOf("@");
46
+ if (atIndex <= 0 || atIndex === email.length - 1) return "[redacted-email]";
47
+ const local = email.slice(0, atIndex);
48
+ const domain = email.slice(atIndex + 1);
49
+ return `${local.slice(0, 1)}***@${domain}`;
50
+ }
51
+
42
52
  export function registerAiIdentityLifecycleCommands({ identity, legalHold }) {
43
53
  identity
44
54
  .command("list")
@@ -75,8 +85,9 @@ identity
75
85
  return;
76
86
  }
77
87
  for (const item of identities) {
88
+ const displayAddress = maskEmailForDisplay(item.emailAddress);
78
89
  console.log(
79
- `- ${item.identityId} | ${item.emailAddress || "unknown-email"} | ${item.status} | ${
90
+ `- ${item.identityId} | ${displayAddress} | ${item.status} | ${
80
91
  item.projectId || "no-project"
81
92
  }`
82
93
  );
@@ -118,7 +129,8 @@ identity
118
129
 
119
130
  console.log(pc.bold("AIdenID identity"));
120
131
  console.log(pc.gray(`Registry: ${registryPath}`));
121
- console.log(`${identityRecord.identityId} | ${identityRecord.emailAddress || "unknown-email"}`);
132
+ const displayAddress = maskEmailForDisplay(identityRecord.emailAddress);
133
+ console.log(`${identityRecord.identityId} | ${displayAddress}`);
122
134
  console.log(`status=${identityRecord.status} project=${identityRecord.projectId || "n/a"}`);
123
135
  });
124
136
 
@@ -11,6 +11,7 @@ import {
11
11
  buildAidenIdRegistryTemplate,
12
12
  buildMcpToolRegistrySchema,
13
13
  buildMcpServerConfigTemplate,
14
+ buildSentinelayerSessionRegistryTemplate,
14
15
  buildVsCodeMcpBridgeTemplate,
15
16
  readJsonFile,
16
17
  resolveDefaultAidenIdAdapterContractPath,
@@ -23,6 +24,7 @@ import {
23
24
  validateMcpToolRegistry,
24
25
  writeJsonFile,
25
26
  } from "../mcp/registry.js";
27
+ import { runMcpStdioServer } from "../mcp/session-stdio-server.js";
26
28
  import { resolveOutputRoot } from "../config/service.js";
27
29
 
28
30
  function shouldEmitJson(options, command) {
@@ -220,6 +222,38 @@ export function registerMcpCommand(program) {
220
222
  console.log(pc.gray("Next: validate with registry cross-check before runtime wiring."));
221
223
  });
222
224
 
225
+ registry
226
+ .command("init-session")
227
+ .description("Write the built-in SentinelLayer session MCP tool registry")
228
+ .option("--path <path>", "Destination file path override")
229
+ .option("--output-dir <path>", "Optional artifact output root override")
230
+ .option("--force", "Overwrite destination file if it already exists")
231
+ .option("--json", "Emit machine-readable output")
232
+ .action(async (options, command) => {
233
+ const defaultPath = await resolveDefaultMcpOutputPath({
234
+ cwd: process.cwd(),
235
+ outputDir: options.outputDir,
236
+ env: process.env,
237
+ });
238
+ const outputPath = normalizeOutputPath(options.path, defaultPath.replace(".schema", ".session-tools"));
239
+ const template = buildSentinelayerSessionRegistryTemplate();
240
+ validateMcpToolRegistry(template);
241
+ const writtenPath = await writeJsonFile(outputPath, template, { force: Boolean(options.force) });
242
+ const payload = {
243
+ command: "mcp registry init-session",
244
+ outputPath: writtenPath,
245
+ toolCount: template.tools.length,
246
+ tools: template.tools.map((tool) => tool.name),
247
+ };
248
+
249
+ if (shouldEmitJson(options, command)) {
250
+ console.log(JSON.stringify(payload, null, 2));
251
+ return;
252
+ }
253
+ console.log(pc.green(`Wrote SentinelLayer session MCP registry template: ${writtenPath}`));
254
+ console.log(pc.gray("Next: run 'sl mcp server init --id sentinelayer-session --registry-file <path>'."));
255
+ });
256
+
223
257
  registry
224
258
  .command("validate")
225
259
  .description("Validate an MCP tool registry JSON payload against Sentinelayer contract")
@@ -422,6 +456,32 @@ export function registerMcpCommand(program) {
422
456
  console.log(pc.gray(`File: ${loaded.path}`));
423
457
  });
424
458
 
459
+ server
460
+ .command("run")
461
+ .description("Run the SentinelLayer MCP stdio server")
462
+ .option("--config <path>", "MCP server config JSON to validate before startup")
463
+ .option("--path <path>", "Workspace path used for session auth and local caches", ".")
464
+ .option("--framing <mode>", "stdio framing: newline or content-length", "newline")
465
+ .action(async (options) => {
466
+ const targetPath = path.resolve(process.cwd(), String(options.path || "."));
467
+ const configPath = String(options.config || "").trim();
468
+ if (configPath) {
469
+ const loaded = await readJsonFile(path.resolve(process.cwd(), configPath));
470
+ const parsed = validateMcpServerConfig(loaded.data);
471
+ if (parsed.transport.mode !== "stdio") {
472
+ throw new Error("mcp server run requires a stdio server config.");
473
+ }
474
+ }
475
+ const framing = String(options.framing || "newline").trim().toLowerCase();
476
+ if (!["newline", "content-length"].includes(framing)) {
477
+ throw new Error("framing must be 'newline' or 'content-length'.");
478
+ }
479
+ await runMcpStdioServer({
480
+ targetPath,
481
+ framing,
482
+ });
483
+ });
484
+
425
485
  const bridge = mcp.command("bridge").description("Generate MCP bridge configuration wrappers");
426
486
 
427
487
  bridge