@sirius-mcp/mcp 1.4.0 → 1.5.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/LICENSE +7 -0
- package/README.md +177 -0
- package/dist/adapters/sqlite/ftsIndex.js +17 -17
- package/dist/installer/backup.d.ts +4 -0
- package/dist/installer/backup.d.ts.map +1 -0
- package/dist/installer/backup.js +88 -0
- package/dist/installer/backup.js.map +1 -0
- package/dist/installer/cli-parser.d.ts +3 -0
- package/dist/installer/cli-parser.d.ts.map +1 -0
- package/dist/installer/cli-parser.js +71 -0
- package/dist/installer/cli-parser.js.map +1 -0
- package/dist/installer/cli.d.ts +10 -0
- package/dist/installer/cli.d.ts.map +1 -0
- package/dist/installer/cli.js +208 -0
- package/dist/installer/cli.js.map +1 -0
- package/dist/installer/detector.d.ts +8 -0
- package/dist/installer/detector.d.ts.map +1 -0
- package/dist/installer/detector.js +169 -0
- package/dist/installer/detector.js.map +1 -0
- package/dist/installer/diff.d.ts +3 -0
- package/dist/installer/diff.d.ts.map +1 -0
- package/dist/installer/diff.js +85 -0
- package/dist/installer/diff.js.map +1 -0
- package/dist/installer/dispatch.d.ts +5 -0
- package/dist/installer/dispatch.d.ts.map +1 -0
- package/dist/installer/dispatch.js +96 -0
- package/dist/installer/dispatch.js.map +1 -0
- package/dist/installer/index.d.ts +10 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +20 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/patchers/index.d.ts +7 -0
- package/dist/installer/patchers/index.d.ts.map +1 -0
- package/dist/installer/patchers/index.js +26 -0
- package/dist/installer/patchers/index.js.map +1 -0
- package/dist/installer/patchers/jsonc.d.ts +4 -0
- package/dist/installer/patchers/jsonc.d.ts.map +1 -0
- package/dist/installer/patchers/jsonc.js +126 -0
- package/dist/installer/patchers/jsonc.js.map +1 -0
- package/dist/installer/patchers/toml.d.ts +3 -0
- package/dist/installer/patchers/toml.d.ts.map +1 -0
- package/dist/installer/patchers/toml.js +88 -0
- package/dist/installer/patchers/toml.js.map +1 -0
- package/dist/installer/patchers/yaml.d.ts +3 -0
- package/dist/installer/patchers/yaml.d.ts.map +1 -0
- package/dist/installer/patchers/yaml.js +29 -0
- package/dist/installer/patchers/yaml.js.map +1 -0
- package/dist/installer/targets-base.d.ts +8 -0
- package/dist/installer/targets-base.d.ts.map +1 -0
- package/dist/installer/targets-base.js +37 -0
- package/dist/installer/targets-base.js.map +1 -0
- package/dist/installer/targets-cli.d.ts +3 -0
- package/dist/installer/targets-cli.d.ts.map +1 -0
- package/dist/installer/targets-cli.js +126 -0
- package/dist/installer/targets-cli.js.map +1 -0
- package/dist/installer/targets-ide.d.ts +3 -0
- package/dist/installer/targets-ide.d.ts.map +1 -0
- package/dist/installer/targets-ide.js +180 -0
- package/dist/installer/targets-ide.js.map +1 -0
- package/dist/installer/targets.d.ts +7 -0
- package/dist/installer/targets.d.ts.map +1 -0
- package/dist/installer/targets.js +11 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/installer/tui.d.ts +8 -0
- package/dist/installer/tui.d.ts.map +1 -0
- package/dist/installer/tui.js +257 -0
- package/dist/installer/tui.js.map +1 -0
- package/dist/installer/types.d.ts +95 -0
- package/dist/installer/types.d.ts.map +1 -0
- package/dist/installer/types.js +7 -0
- package/dist/installer/types.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +11 -1
- package/dist/server.js.map +1 -1
- package/package.json +69 -67
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Sirius MCP Server License
|
|
2
|
+
Copyright (c) 2026 Sirius MCP Contributors
|
|
3
|
+
|
|
4
|
+
Proprietary Software.
|
|
5
|
+
Free for solo developer and local development use.
|
|
6
|
+
Distribution and enterprise use are subject to commercial licensing terms.
|
|
7
|
+
See https://siriusmcp.dev for more details.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @sirius-mcp/mcp
|
|
2
|
+
|
|
3
|
+
> **Sirius MCP** — Persistent project-level context memory for AI coding agents.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@sirius-mcp/mcp)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://modelcontextprotocol.io)
|
|
8
|
+
|
|
9
|
+
Sirius is a Model Context Protocol (MCP) server that gives your AI agent (Claude Code, Cursor, Windsurf, OpenCode, VS Code, Antigravity) long-term, auditable project memory. It stores architecture decisions, features, and business rules in a plain-text `.context/` markdown tree directly in your repository, indexed locally with SQLite FTS5.
|
|
10
|
+
|
|
11
|
+
Solo use is completely free with zero sign-up or license key needed.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
Run or configure Sirius directly using `npx`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"sirius": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@sirius-mcp/mcp"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`-y` automatically accepts the package installation prompt so the agent startup is non-blocking.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## IDE & MCP Client Configuration
|
|
35
|
+
|
|
36
|
+
Add Sirius to your preferred environment:
|
|
37
|
+
|
|
38
|
+
### Claude Code (`.mcp.json` or `~/.claude.json`)
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"sirius": {
|
|
43
|
+
"command": "npx",
|
|
44
|
+
"args": ["-y", "@sirius-mcp/mcp"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Cursor (`.cursor/mcp.json` or Settings > Features > MCP)
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"sirius": {
|
|
55
|
+
"command": "npx",
|
|
56
|
+
"args": ["-y", "@sirius-mcp/mcp"]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### VS Code (Cline / Roo Code)
|
|
63
|
+
In your Cline / Roo Code MCP settings (`cline_mcp_settings.json`):
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"sirius": {
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": ["-y", "@sirius-mcp/mcp"]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Windsurf (`~/.codeium/windsurf/mcp_config.json`)
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"sirius": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["-y", "@sirius-mcp/mcp"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### OpenCode (`opencode.json`)
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcp": {
|
|
91
|
+
"sirius": {
|
|
92
|
+
"type": "local",
|
|
93
|
+
"command": ["npx", "-y", "@sirius-mcp/mcp"],
|
|
94
|
+
"enabled": true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Docker Alternative
|
|
103
|
+
|
|
104
|
+
If you prefer running Sirius inside a container (for persistent servers or VPS deployments):
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
docker run -i --rm \
|
|
108
|
+
--pull=always \
|
|
109
|
+
-p 127.0.0.1:4200:4200 \
|
|
110
|
+
-v "$PWD:/project" \
|
|
111
|
+
-e SIRIUS_PROJECT_ROOT=/project \
|
|
112
|
+
ghcr.io/sirius-mcp/sirius-mcp:latest
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## MCP Tools Provided
|
|
118
|
+
|
|
119
|
+
Once connected, your AI agent gains access to these primary context tools:
|
|
120
|
+
|
|
121
|
+
| Tool | Description |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `context_list` | Lists all documents in the project memory (filterable by `type`, `status`, `path_prefix`). |
|
|
124
|
+
| `context_get` | Retrieves the full markdown content and frontmatter metadata of a specific document. |
|
|
125
|
+
| `context_search` | Performs full-text search (SQLite FTS5) over all indexed documents. |
|
|
126
|
+
| `context_upsert` | Creates or updates a context document (feature, decision, business-rule, architecture). |
|
|
127
|
+
| `context_crawl` | Crawls codebase or external documentation into context memory. |
|
|
128
|
+
| `reindex` | Rebuilds the local SQLite search index directly from the `.context/` markdown files. |
|
|
129
|
+
| `project_init` | Bootstraps a fresh `.context/` directory in a new repository. |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## MCP Resources Provided
|
|
134
|
+
|
|
135
|
+
- `context://document/{type}/{id}` — Direct URI to read any context document.
|
|
136
|
+
- `context://system/update-status` — Real-time update check and release metadata signal.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Embedded Living Architecture Web Portal
|
|
141
|
+
|
|
142
|
+
Sirius includes a built-in web portal that visualizes your architecture blueprints, dependencies, decisions, and system rules in real-time.
|
|
143
|
+
|
|
144
|
+
When Sirius runs, navigate to:
|
|
145
|
+
```
|
|
146
|
+
http://localhost:4200
|
|
147
|
+
```
|
|
148
|
+
to explore your project's living knowledge base visually.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Storage & Invariants
|
|
153
|
+
|
|
154
|
+
- **Markdown as Single Source of Truth**: Documents live in `.context/` inside your project git repository.
|
|
155
|
+
- **Auditable & Git-friendly**: Every decision or rule change can be committed, reviewed in PRs, and tracked in git history.
|
|
156
|
+
- **Ephemeral Index**: The local SQLite database (`.context/.index/sirius.db`) is completely disposable and gitignored; it can be rebuilt instantly with `reindex`.
|
|
157
|
+
- **Zero-network by default**: Solo tier runs completely local on your machine.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Requirements
|
|
162
|
+
|
|
163
|
+
- **Node.js**: >= 22.0.0 (npm included)
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Documentation
|
|
168
|
+
|
|
169
|
+
Full documentation, guides, and deployment recipes are available at:
|
|
170
|
+
- Documentation: [siriusmcp.dev/docs/deploy](https://siriusmcp.dev/docs/deploy)
|
|
171
|
+
- GitHub Repository: [github.com/sirius-mcp/sirius-mcp](https://github.com/sirius-mcp/sirius-mcp)
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
Proprietary. Free for solo and local developer use.
|
|
@@ -59,11 +59,11 @@ export class FtsIndexAdapter {
|
|
|
59
59
|
if (!safeQuery) {
|
|
60
60
|
return [];
|
|
61
61
|
}
|
|
62
|
-
const stmt = this.db.prepare(`SELECT id, type, status, title, path,
|
|
63
|
-
snippet(documents_fts, 4, '<<<', '>>>', '...', 32) as snippet
|
|
64
|
-
FROM documents_fts
|
|
65
|
-
WHERE documents_fts MATCH ?
|
|
66
|
-
ORDER BY rank
|
|
62
|
+
const stmt = this.db.prepare(`SELECT id, type, status, title, path,
|
|
63
|
+
snippet(documents_fts, 4, '<<<', '>>>', '...', 32) as snippet
|
|
64
|
+
FROM documents_fts
|
|
65
|
+
WHERE documents_fts MATCH ?
|
|
66
|
+
ORDER BY rank
|
|
67
67
|
LIMIT ?`);
|
|
68
68
|
let rows = stmt.all(safeQuery, limit);
|
|
69
69
|
if (rows.length === 0) {
|
|
@@ -88,14 +88,14 @@ export class FtsIndexAdapter {
|
|
|
88
88
|
}
|
|
89
89
|
rebuild(docs) {
|
|
90
90
|
this.ensureOpen();
|
|
91
|
-
const createFts = this.db.prepare(`CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
92
|
-
id UNINDEXED, type UNINDEXED, status UNINDEXED,
|
|
93
|
-
title, body, path UNINDEXED,
|
|
94
|
-
tokenize = 'porter unicode61'
|
|
91
|
+
const createFts = this.db.prepare(`CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
92
|
+
id UNINDEXED, type UNINDEXED, status UNINDEXED,
|
|
93
|
+
title, body, path UNINDEXED,
|
|
94
|
+
tokenize = 'porter unicode61'
|
|
95
95
|
)`);
|
|
96
96
|
const dropFts = this.db.prepare("DROP TABLE IF EXISTS documents_fts");
|
|
97
97
|
const insertStmt = this.db.prepare("INSERT INTO documents_fts (id, type, status, title, body, path) VALUES (?, ?, ?, ?, ?, ?)");
|
|
98
|
-
const setVersion = this.db.prepare(`INSERT INTO index_meta (key, value) VALUES ('schema_version', ?)
|
|
98
|
+
const setVersion = this.db.prepare(`INSERT INTO index_meta (key, value) VALUES ('schema_version', ?)
|
|
99
99
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`);
|
|
100
100
|
const transaction = this.db.transaction((documents) => {
|
|
101
101
|
dropFts.run();
|
|
@@ -110,14 +110,14 @@ export class FtsIndexAdapter {
|
|
|
110
110
|
this._needsRebuild = false;
|
|
111
111
|
}
|
|
112
112
|
initializeSchema() {
|
|
113
|
-
this.db.exec(`CREATE TABLE IF NOT EXISTS index_meta (
|
|
114
|
-
key TEXT PRIMARY KEY,
|
|
115
|
-
value TEXT NOT NULL
|
|
113
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS index_meta (
|
|
114
|
+
key TEXT PRIMARY KEY,
|
|
115
|
+
value TEXT NOT NULL
|
|
116
116
|
)`);
|
|
117
|
-
const createFts = this.db.prepare(`CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
118
|
-
id UNINDEXED, type UNINDEXED, status UNINDEXED,
|
|
119
|
-
title, body, path UNINDEXED,
|
|
120
|
-
tokenize = 'porter unicode61'
|
|
117
|
+
const createFts = this.db.prepare(`CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
|
|
118
|
+
id UNINDEXED, type UNINDEXED, status UNINDEXED,
|
|
119
|
+
title, body, path UNINDEXED,
|
|
120
|
+
tokenize = 'porter unicode61'
|
|
121
121
|
)`);
|
|
122
122
|
createFts.run();
|
|
123
123
|
const row = this.db
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function formatBackupTimestamp(date?: Date): string;
|
|
2
|
+
export declare function createBackup(filePath: string): Promise<string | null>;
|
|
3
|
+
export declare function atomicWrite(filePath: string, content: string): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/installer/backup.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,IAAiB,GAAG,MAAM,CASrE;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkB3E;AAkCD,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BlF"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
export function formatBackupTimestamp(date = new Date()) {
|
|
5
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
6
|
+
const yyyy = date.getFullYear();
|
|
7
|
+
const mm = pad(date.getMonth() + 1);
|
|
8
|
+
const dd = pad(date.getDate());
|
|
9
|
+
const hh = pad(date.getHours());
|
|
10
|
+
const min = pad(date.getMinutes());
|
|
11
|
+
const ss = pad(date.getSeconds());
|
|
12
|
+
return `${yyyy}${mm}${dd}${hh}${min}${ss}`;
|
|
13
|
+
}
|
|
14
|
+
export async function createBackup(filePath) {
|
|
15
|
+
if (!fs.existsSync(filePath)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const parentDir = path.dirname(filePath);
|
|
19
|
+
const baseName = path.basename(filePath);
|
|
20
|
+
let timestamp = formatBackupTimestamp();
|
|
21
|
+
let backupPath = path.join(parentDir, `${baseName}.bak.${timestamp}`);
|
|
22
|
+
let counter = 1;
|
|
23
|
+
while (fs.existsSync(backupPath)) {
|
|
24
|
+
timestamp = `${formatBackupTimestamp()}_${counter++}`;
|
|
25
|
+
backupPath = path.join(parentDir, `${baseName}.bak.${timestamp}`);
|
|
26
|
+
}
|
|
27
|
+
await fs.promises.copyFile(filePath, backupPath);
|
|
28
|
+
return backupPath;
|
|
29
|
+
}
|
|
30
|
+
function sleep(ms) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
|
+
}
|
|
33
|
+
async function writeAndSyncTemp(tempPath, content) {
|
|
34
|
+
const handle = await fs.promises.open(tempPath, "w");
|
|
35
|
+
try {
|
|
36
|
+
await handle.writeFile(content, "utf8");
|
|
37
|
+
await handle.sync();
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await handle.close();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function tryRenameWithBackoff(tempPath, targetPath) {
|
|
44
|
+
const backoffs = [100, 200, 400];
|
|
45
|
+
for (let i = 0; i <= backoffs.length; i++) {
|
|
46
|
+
try {
|
|
47
|
+
await fs.promises.rename(tempPath, targetPath);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const code = err?.code;
|
|
52
|
+
const isLockError = code === "EBUSY" || code === "EPERM";
|
|
53
|
+
if (!isLockError || i === backoffs.length) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
await sleep(backoffs[i]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
export async function atomicWrite(filePath, content) {
|
|
62
|
+
const parentDir = path.dirname(filePath);
|
|
63
|
+
await fs.promises.mkdir(parentDir, { recursive: true, mode: 0o700 });
|
|
64
|
+
const tempPath = path.join(parentDir, `${path.basename(filePath)}.tmp.${process.pid}.${crypto.randomUUID()}`);
|
|
65
|
+
try {
|
|
66
|
+
await writeAndSyncTemp(tempPath, content);
|
|
67
|
+
if (fs.existsSync(filePath)) {
|
|
68
|
+
const stat = await fs.promises.stat(filePath);
|
|
69
|
+
await fs.promises.chmod(tempPath, stat.mode);
|
|
70
|
+
}
|
|
71
|
+
const renamed = await tryRenameWithBackoff(tempPath, filePath);
|
|
72
|
+
if (!renamed) {
|
|
73
|
+
// Fallback: direct in-place buffered write
|
|
74
|
+
await fs.promises.writeFile(filePath, content, "utf8");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
if (fs.existsSync(tempPath)) {
|
|
79
|
+
try {
|
|
80
|
+
await fs.promises.unlink(tempPath);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// cleanup best-effort
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/installer/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,UAAU,qBAAqB,CAAC,OAAa,IAAI,IAAI,EAAE;IAC3D,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAClC,OAAO,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,SAAS,GAAG,qBAAqB,EAAE,CAAC;IACxC,IAAI,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,QAAQ,SAAS,EAAE,CAAC,CAAC;IAEtE,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,SAAS,GAAG,GAAG,qBAAqB,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC;QACtD,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,QAAQ,SAAS,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACjD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAgB,EAAE,OAAe;IAC/D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,QAAgB,EAAE,UAAkB;IACtE,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,IAAI,GAAI,GAAyB,EAAE,IAAI,CAAC;YAC9C,MAAM,WAAW,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,CAAC;YACzD,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC1C,MAAM;YACR,CAAC;YACD,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,OAAe;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAErE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,SAAS,EACT,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,EAAE,CACvE,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC9C,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,2CAA2C;YAC3C,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-parser.d.ts","sourceRoot":"","sources":["../../src/installer/cli-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,YAAY,CAAC;AAkDpB,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CA6BnE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {} from "./types.js";
|
|
2
|
+
function parseValuedFlag(arg, val, options) {
|
|
3
|
+
if (arg === "--ide" || arg === "--clients") {
|
|
4
|
+
options.targets = val?.split(",").map((s) => s.trim().toLowerCase());
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
if (arg === "--category") {
|
|
8
|
+
options.category = val?.toLowerCase();
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (arg === "--scope") {
|
|
12
|
+
options.scope = val?.toLowerCase() ?? "global";
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function parseBooleanFlag(arg, options) {
|
|
18
|
+
if (arg === "--all") {
|
|
19
|
+
options.all = true;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (arg === "--yes" || arg === "-y") {
|
|
23
|
+
options.yes = true;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (arg === "--dry-run") {
|
|
27
|
+
options.dryRun = true;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (arg === "--json") {
|
|
31
|
+
options.json = true;
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (arg === "--force") {
|
|
35
|
+
options.force = true;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (arg === "--stdio") {
|
|
39
|
+
options.stdio = true;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export function parseInstallerArgs(args) {
|
|
45
|
+
const options = {
|
|
46
|
+
subcommand: "install",
|
|
47
|
+
scope: "global",
|
|
48
|
+
all: false,
|
|
49
|
+
yes: false,
|
|
50
|
+
dryRun: false,
|
|
51
|
+
json: false,
|
|
52
|
+
force: false,
|
|
53
|
+
stdio: false,
|
|
54
|
+
};
|
|
55
|
+
const subcommands = ["install", "doctor", "uninstall", "setup"];
|
|
56
|
+
for (let i = 0; i < args.length; i++) {
|
|
57
|
+
const arg = args[i];
|
|
58
|
+
if (i === 0 && subcommands.includes(arg)) {
|
|
59
|
+
options.subcommand = arg === "setup" ? "install" : arg;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (parseValuedFlag(arg, args[i + 1], options)) {
|
|
63
|
+
i++;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
parseBooleanFlag(arg, options);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return options;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=cli-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-parser.js","sourceRoot":"","sources":["../../src/installer/cli-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type InstallerOptions, type PlatformEnv } from "./types.js";
|
|
2
|
+
export { parseInstallerArgs } from "./cli-parser.js";
|
|
3
|
+
export interface CliIo {
|
|
4
|
+
writeStdout: (data: string) => void;
|
|
5
|
+
writeStderr: (data: string) => void;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
env?: PlatformEnv;
|
|
8
|
+
}
|
|
9
|
+
export declare function runInstallerCli(options: InstallerOptions, ioOverrides?: Partial<CliIo>): Promise<number>;
|
|
10
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/installer/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AAOpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAyJD,wBAAsB,eAAe,CACnC,OAAO,EAAE,gBAAgB,EACzB,WAAW,GAAE,OAAO,CAAC,KAAK,CAAM,GAC/B,OAAO,CAAC,MAAM,CAAC,CAkFjB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {} from "./types.js";
|
|
4
|
+
import { ALL_TARGETS, getTargetById, resolveTargetEnvironment } from "./targets.js";
|
|
5
|
+
import { detectAllTargets } from "./detector.js";
|
|
6
|
+
import { patchContent, removeContent } from "./patchers/index.js";
|
|
7
|
+
import { createBackup, atomicWrite } from "./backup.js";
|
|
8
|
+
import { generateUnifiedDiff, colorizeDiff } from "./diff.js";
|
|
9
|
+
export { parseInstallerArgs } from "./cli-parser.js";
|
|
10
|
+
const defaultIo = {
|
|
11
|
+
writeStdout: (d) => process.stdout.write(d),
|
|
12
|
+
writeStderr: (d) => process.stderr.write(d),
|
|
13
|
+
};
|
|
14
|
+
function resolveSelectedTargets(options) {
|
|
15
|
+
if (options.targets && options.targets.length > 0) {
|
|
16
|
+
return options.targets
|
|
17
|
+
.map((id) => getTargetById(id))
|
|
18
|
+
.filter((t) => t !== undefined);
|
|
19
|
+
}
|
|
20
|
+
if (options.category === "cli") {
|
|
21
|
+
return ALL_TARGETS.filter((t) => t.category === "cli");
|
|
22
|
+
}
|
|
23
|
+
if (options.category === "ides") {
|
|
24
|
+
return ALL_TARGETS.filter((t) => t.category === "ide");
|
|
25
|
+
}
|
|
26
|
+
return [...ALL_TARGETS];
|
|
27
|
+
}
|
|
28
|
+
function buildPatcherEnv(target, env) {
|
|
29
|
+
if (target.needsMacGuiEnvPatch && env.platform === "darwin") {
|
|
30
|
+
const nodeDir = path.dirname(env.execPath);
|
|
31
|
+
const defaults = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
32
|
+
return { PATH: Array.from(new Set([nodeDir, ...defaults])).join(":") };
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
async function handleDryRun(target, paths, patched, raw, io) {
|
|
37
|
+
const diff = generateUnifiedDiff(raw, patched, path.basename(paths.configPath));
|
|
38
|
+
if (!io.writeStdout || io === defaultIo) {
|
|
39
|
+
io.writeStderr(`\n[dry-run] Proposed changes for ${target.displayName} (${paths.configPath}):\n`);
|
|
40
|
+
io.writeStderr(colorizeDiff(diff));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
targetId: target.id,
|
|
44
|
+
displayName: target.displayName,
|
|
45
|
+
category: target.category,
|
|
46
|
+
format: target.format,
|
|
47
|
+
targetPath: paths.configPath,
|
|
48
|
+
action: "CONFIGURED",
|
|
49
|
+
message: "Dry-run preview generated",
|
|
50
|
+
diff,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function executeInstallTarget(target, options, env, io) {
|
|
54
|
+
const paths = target.resolvePaths(options.scope, env);
|
|
55
|
+
let raw = "";
|
|
56
|
+
if (fs.existsSync(paths.configPath)) {
|
|
57
|
+
raw = fs.readFileSync(paths.configPath, "utf8");
|
|
58
|
+
}
|
|
59
|
+
const patcherEnv = buildPatcherEnv(target, env);
|
|
60
|
+
const patched = patchContent(raw, {
|
|
61
|
+
targetId: target.id,
|
|
62
|
+
patcherType: target.patcherType,
|
|
63
|
+
format: target.format,
|
|
64
|
+
env: patcherEnv,
|
|
65
|
+
});
|
|
66
|
+
if (options.dryRun) {
|
|
67
|
+
return handleDryRun(target, paths, patched, raw, io);
|
|
68
|
+
}
|
|
69
|
+
let backupPath;
|
|
70
|
+
if (fs.existsSync(paths.configPath)) {
|
|
71
|
+
const bak = await createBackup(paths.configPath);
|
|
72
|
+
if (bak)
|
|
73
|
+
backupPath = bak;
|
|
74
|
+
}
|
|
75
|
+
await atomicWrite(paths.configPath, patched);
|
|
76
|
+
return {
|
|
77
|
+
targetId: target.id,
|
|
78
|
+
displayName: target.displayName,
|
|
79
|
+
category: target.category,
|
|
80
|
+
format: target.format,
|
|
81
|
+
targetPath: paths.configPath,
|
|
82
|
+
backupPath,
|
|
83
|
+
action: "CONFIGURED",
|
|
84
|
+
message: `Configured ${target.displayName}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async function executeUninstallTarget(target, options, env) {
|
|
88
|
+
const paths = target.resolvePaths(options.scope, env);
|
|
89
|
+
if (!fs.existsSync(paths.configPath)) {
|
|
90
|
+
return {
|
|
91
|
+
targetId: target.id,
|
|
92
|
+
displayName: target.displayName,
|
|
93
|
+
category: target.category,
|
|
94
|
+
format: target.format,
|
|
95
|
+
targetPath: paths.configPath,
|
|
96
|
+
action: "SKIPPED",
|
|
97
|
+
message: "Config file does not exist",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const raw = fs.readFileSync(paths.configPath, "utf8");
|
|
101
|
+
const uninstalled = removeContent(raw, {
|
|
102
|
+
targetId: target.id,
|
|
103
|
+
patcherType: target.patcherType,
|
|
104
|
+
format: target.format,
|
|
105
|
+
});
|
|
106
|
+
let backupPath;
|
|
107
|
+
const bak = await createBackup(paths.configPath);
|
|
108
|
+
if (bak)
|
|
109
|
+
backupPath = bak;
|
|
110
|
+
await atomicWrite(paths.configPath, uninstalled);
|
|
111
|
+
return {
|
|
112
|
+
targetId: target.id,
|
|
113
|
+
displayName: target.displayName,
|
|
114
|
+
category: target.category,
|
|
115
|
+
format: target.format,
|
|
116
|
+
targetPath: paths.configPath,
|
|
117
|
+
backupPath,
|
|
118
|
+
action: "REMOVED",
|
|
119
|
+
message: `Removed Sirius configuration from ${target.displayName}`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function emitReport(report, options, io) {
|
|
123
|
+
if (options.json) {
|
|
124
|
+
io.writeStdout(JSON.stringify(report, null, 2) + "\n");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
io.writeStderr(`\n[sirius] Completed: ${report.summary.configured} configured, ${report.summary.skipped} skipped, ${report.summary.failed} failed.\n`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export async function runInstallerCli(options, ioOverrides = {}) {
|
|
131
|
+
const io = { ...defaultIo, ...ioOverrides };
|
|
132
|
+
const env = io.env ?? {
|
|
133
|
+
...resolveTargetEnvironment(),
|
|
134
|
+
...(io.cwd ? { cwd: io.cwd } : {}),
|
|
135
|
+
};
|
|
136
|
+
if (options.subcommand === "doctor") {
|
|
137
|
+
const detected = await detectAllTargets(options.scope, env);
|
|
138
|
+
const results = detected.map((d) => ({
|
|
139
|
+
targetId: d.target.id,
|
|
140
|
+
displayName: d.target.displayName,
|
|
141
|
+
category: d.target.category,
|
|
142
|
+
format: d.target.format,
|
|
143
|
+
targetPath: d.configPath,
|
|
144
|
+
action: d.state === "ALREADY_CONFIGURED" ? "ALREADY_CONFIGURED" : "SKIPPED",
|
|
145
|
+
message: `State: ${d.state}`,
|
|
146
|
+
}));
|
|
147
|
+
const report = {
|
|
148
|
+
version: "1.4.1",
|
|
149
|
+
timestamp: new Date().toISOString(),
|
|
150
|
+
platform: env.platform,
|
|
151
|
+
arch: process.arch,
|
|
152
|
+
dryRun: false,
|
|
153
|
+
scope: options.scope,
|
|
154
|
+
summary: {
|
|
155
|
+
totalScanned: detected.length,
|
|
156
|
+
configured: detected.filter((d) => d.state === "ALREADY_CONFIGURED").length,
|
|
157
|
+
skipped: detected.filter((d) => d.state !== "ALREADY_CONFIGURED").length,
|
|
158
|
+
failed: 0,
|
|
159
|
+
},
|
|
160
|
+
targets: results,
|
|
161
|
+
};
|
|
162
|
+
emitReport(report, options, io);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
const selected = resolveSelectedTargets(options);
|
|
166
|
+
const results = [];
|
|
167
|
+
for (const target of selected) {
|
|
168
|
+
try {
|
|
169
|
+
if (options.subcommand === "uninstall") {
|
|
170
|
+
results.push(await executeUninstallTarget(target, options, env));
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
results.push(await executeInstallTarget(target, options, env, io));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
results.push({
|
|
178
|
+
targetId: target.id,
|
|
179
|
+
displayName: target.displayName,
|
|
180
|
+
category: target.category,
|
|
181
|
+
format: target.format,
|
|
182
|
+
targetPath: target.resolvePaths(options.scope, env).configPath,
|
|
183
|
+
action: "FAILED",
|
|
184
|
+
message: err instanceof Error ? err.message : String(err),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const failedCount = results.filter((r) => r.action === "FAILED").length;
|
|
189
|
+
const configuredCount = results.filter((r) => r.action === "CONFIGURED" || r.action === "REMOVED").length;
|
|
190
|
+
const report = {
|
|
191
|
+
version: "1.4.1",
|
|
192
|
+
timestamp: new Date().toISOString(),
|
|
193
|
+
platform: env.platform,
|
|
194
|
+
arch: process.arch,
|
|
195
|
+
dryRun: options.dryRun,
|
|
196
|
+
scope: options.scope,
|
|
197
|
+
summary: {
|
|
198
|
+
totalScanned: selected.length,
|
|
199
|
+
configured: configuredCount,
|
|
200
|
+
skipped: selected.length - configuredCount - failedCount,
|
|
201
|
+
failed: failedCount,
|
|
202
|
+
},
|
|
203
|
+
targets: results,
|
|
204
|
+
};
|
|
205
|
+
emitReport(report, options, io);
|
|
206
|
+
return failedCount > 0 ? 1 : 0;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=cli.js.map
|