systemview 2.2.4 → 2.3.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/api/connections.json +1 -1
- package/build/asset-manifest.json +6 -6
- package/build/index.html +1 -1
- package/build/static/css/{main.1916c1d1.css → main.42546597.css} +2 -2
- package/build/static/css/{main.1916c1d1.css.map → main.42546597.css.map} +1 -1
- package/build/static/js/{main.4b6a5163.js → main.3d700bb2.js} +3 -3
- package/build/static/js/{main.4b6a5163.js.map → main.3d700bb2.js.map} +1 -1
- package/cli/connectService.js +18 -1
- package/cli/cookieClient.js +14 -45
- package/cli/index.js +24 -12
- package/cli/manifest.js +10 -3
- package/cli/manifestHeaders.js +117 -0
- package/cli/probe.js +3 -0
- package/cli/utils/cli.js +6 -1
- package/package.json +1 -1
- /package/build/static/js/{main.4b6a5163.js.LICENSE.txt → main.3d700bb2.js.LICENSE.txt} +0 -0
package/cli/connectService.js
CHANGED
|
@@ -3,6 +3,7 @@ const { createCookieHttpClient } = require("./cookieClient");
|
|
|
3
3
|
const cookieHttpClient = createCookieHttpClient();
|
|
4
4
|
const Client = createClient(cookieHttpClient);
|
|
5
5
|
const log = require("./logger");
|
|
6
|
+
const manifestCommands = require("./manifest");
|
|
6
7
|
|
|
7
8
|
async function getUiSvc(uiUrl) {
|
|
8
9
|
try {
|
|
@@ -33,7 +34,7 @@ function printSummary(entries) {
|
|
|
33
34
|
console.log("");
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
module.exports = async function connectService(target, { useManifest, force, connectedUrls, uiUrl } = {}) {
|
|
37
|
+
module.exports = async function connectService(target, { useManifest, save, force, connectedUrls, uiUrl } = {}) {
|
|
37
38
|
const uiSvc = await getUiSvc(uiUrl);
|
|
38
39
|
|
|
39
40
|
// connect (no args) — connect to every service stored in UI
|
|
@@ -133,6 +134,17 @@ module.exports = async function connectService(target, { useManifest, force, con
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
printSummary(summary);
|
|
137
|
+
if (save) {
|
|
138
|
+
// --save persists THIS connection to the local manifest. The subject is the connection
|
|
139
|
+
// (the services); the applied headers + captured session cookie tag along via save().
|
|
140
|
+
const services = manifest.services.map((s) => ({
|
|
141
|
+
projectCode: manifest.projectCode,
|
|
142
|
+
serviceId: s.serviceId,
|
|
143
|
+
system: s.system,
|
|
144
|
+
specList: s.specList || { tests: [], docs: [] },
|
|
145
|
+
}));
|
|
146
|
+
manifestCommands.save(services);
|
|
147
|
+
}
|
|
136
148
|
} else {
|
|
137
149
|
log.warn("No plugin manifest found. Storing as connected-services.");
|
|
138
150
|
const connectionData = await HttpClient.request({ url: target });
|
|
@@ -165,6 +177,11 @@ module.exports = async function connectService(target, { useManifest, force, con
|
|
|
165
177
|
await notifyUi(uiSvc, project);
|
|
166
178
|
log.success(`connected-services › ${serviceId}`);
|
|
167
179
|
printSummary([{ projectCode: "connected-services", serviceId, url: target }]);
|
|
180
|
+
if (save) {
|
|
181
|
+
manifestCommands.save([
|
|
182
|
+
{ projectCode: "connected-services", serviceId, system: { connectionData }, specList: { tests: [], docs: [] } },
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
168
185
|
}
|
|
169
186
|
} catch (err) {
|
|
170
187
|
log.error(`Connection failed: ${err.message}`);
|
package/cli/cookieClient.js
CHANGED
|
@@ -1,55 +1,25 @@
|
|
|
1
1
|
const axios = require("axios").default;
|
|
2
2
|
const FormData = require("form-data");
|
|
3
|
-
const fs = require("fs");
|
|
4
3
|
const path = require("path");
|
|
5
|
-
const {
|
|
4
|
+
const { headersFor, captureCookie } = require("./manifestHeaders");
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return JSON.parse(fs.readFileSync(COOKIE_FILE, "utf8"));
|
|
12
|
-
} catch {
|
|
13
|
-
return {};
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function captureCookies(setCookieHeaders) {
|
|
18
|
-
if (!setCookieHeaders || !setCookieHeaders.length) return;
|
|
19
|
-
const existing = readCookies();
|
|
20
|
-
let changed = false;
|
|
21
|
-
setCookieHeaders.forEach((header) => {
|
|
22
|
-
const [pair] = header.split(";");
|
|
23
|
-
const eqIdx = pair.indexOf("=");
|
|
24
|
-
if (eqIdx === -1) return;
|
|
25
|
-
const name = pair.slice(0, eqIdx).trim();
|
|
26
|
-
const value = pair.slice(eqIdx + 1).trim();
|
|
27
|
-
if (name) {
|
|
28
|
-
existing[name] = value;
|
|
29
|
-
changed = true;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
if (changed) fs.writeFileSync(COOKIE_FILE, JSON.stringify(existing, null, 2));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function cookieHeader() {
|
|
36
|
-
const cookies = readCookies();
|
|
37
|
-
const pairs = Object.entries(cookies)
|
|
38
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
39
|
-
.join("; ");
|
|
40
|
-
return pairs || null;
|
|
41
|
-
}
|
|
6
|
+
// HTTP client for the SystemLynx CLI. Per request it attaches the manifest's headers for
|
|
7
|
+
// the target origin (auth tokens, cookies, any scheme — see manifestHeaders.js) and folds
|
|
8
|
+
// any Set-Cookie response back into that store. `extraHeaders` (e.g. --header flags) win on
|
|
9
|
+
// conflict. Cookies + tokens now live in the manifest; there is no separate cookie jar.
|
|
42
10
|
|
|
43
11
|
function createCookieHttpClient(extraHeaders = {}) {
|
|
44
12
|
return {
|
|
45
13
|
request: async ({ method = "get", url, body: data, headers }) => {
|
|
46
14
|
method = method.toLowerCase();
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
15
|
+
// Manifest headers are the base/default; caller-set headers (systemlynx setHeaders, i.e. the
|
|
16
|
+
// CLI `--header` flag) and constructor extraHeaders override them by name. (Matches the upload
|
|
17
|
+
// path below — the request path previously had these flipped, letting the manifest win over
|
|
18
|
+
// an explicit --header.)
|
|
19
|
+
headers = { ...headersFor(url), ...headers, ...extraHeaders };
|
|
50
20
|
try {
|
|
51
21
|
const res = await axios({ url, method, headers, data });
|
|
52
|
-
|
|
22
|
+
captureCookie(url, res.headers["set-cookie"]);
|
|
53
23
|
if (res.status >= 400) throw res.data;
|
|
54
24
|
return res.data;
|
|
55
25
|
} catch (error) {
|
|
@@ -65,16 +35,15 @@ function createCookieHttpClient(extraHeaders = {}) {
|
|
|
65
35
|
if (file) form.append("file", file, path.basename(file.path));
|
|
66
36
|
if (files) files.forEach((f) => form.append("files", f, path.basename(f.path)));
|
|
67
37
|
if (__arguments) form.append("__arguments", JSON.stringify(__arguments));
|
|
68
|
-
const cookie = cookieHeader();
|
|
69
38
|
const allHeaders = {
|
|
70
|
-
...
|
|
71
|
-
...(cookie ? { Cookie: cookie } : {}),
|
|
39
|
+
...headersFor(url),
|
|
72
40
|
...headers,
|
|
41
|
+
...extraHeaders,
|
|
73
42
|
"Content-Type": "multipart/form-data",
|
|
74
43
|
};
|
|
75
44
|
try {
|
|
76
45
|
const res = await axios.post(url, form, { headers: allHeaders });
|
|
77
|
-
|
|
46
|
+
captureCookie(url, res.headers["set-cookie"]);
|
|
78
47
|
if (res.status >= 400) throw res.data;
|
|
79
48
|
return res.data;
|
|
80
49
|
} catch (error) {
|
package/cli/index.js
CHANGED
|
@@ -25,6 +25,17 @@ const DEFAULT_PORT = 3000;
|
|
|
25
25
|
const VERSION = require("../package.json").version;
|
|
26
26
|
const UI_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
27
27
|
|
|
28
|
+
// process.exit() truncates buffered stdout when stdout is a pipe (agents/CI, and large `--json`
|
|
29
|
+
// output). Drain stdout + stderr first, then exit — otherwise big results get cut off mid-stream.
|
|
30
|
+
function flushAndExit(code = 0) {
|
|
31
|
+
let pending = 2;
|
|
32
|
+
const done = () => {
|
|
33
|
+
if (--pending === 0) process.exit(code);
|
|
34
|
+
};
|
|
35
|
+
process.stdout.write("", done);
|
|
36
|
+
process.stderr.write("", done);
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
const MANIFEST_FILE =
|
|
29
40
|
flags.manifest || path.join(process.cwd(), "systemview.manifest.json");
|
|
30
41
|
const connectedUrls = new Set();
|
|
@@ -80,10 +91,10 @@ async function startTest() {
|
|
|
80
91
|
index: flags.index,
|
|
81
92
|
skip: flags.skip,
|
|
82
93
|
});
|
|
83
|
-
|
|
94
|
+
flushAndExit(exitCode);
|
|
84
95
|
} catch (error) {
|
|
85
96
|
log.error("Error executing tests: " + error.message);
|
|
86
|
-
|
|
97
|
+
flushAndExit(1);
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
100
|
|
|
@@ -99,7 +110,7 @@ async function list() {
|
|
|
99
110
|
verbose: flags.verbose,
|
|
100
111
|
json: flags.json,
|
|
101
112
|
});
|
|
102
|
-
|
|
113
|
+
flushAndExit(0);
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
async function open() {
|
|
@@ -119,7 +130,7 @@ async function open() {
|
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
openBrowser(UI_URL, project_code, namespace, connectedServices);
|
|
122
|
-
|
|
133
|
+
flushAndExit(0);
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
async function quitApp() {
|
|
@@ -136,18 +147,18 @@ async function quitApp() {
|
|
|
136
147
|
log.success("SystemView shutdown successful!");
|
|
137
148
|
} else {
|
|
138
149
|
log.error("Remote shutdown failed!");
|
|
139
|
-
|
|
150
|
+
flushAndExit(1);
|
|
140
151
|
}
|
|
141
152
|
} else {
|
|
142
153
|
log.warn(`No SystemView instance found @ ${api}`);
|
|
143
154
|
}
|
|
144
|
-
|
|
155
|
+
flushAndExit(0);
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
(async () => {
|
|
148
159
|
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
149
160
|
console.log(VERSION);
|
|
150
|
-
|
|
161
|
+
flushAndExit(0);
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
init();
|
|
@@ -168,17 +179,18 @@ async function quitApp() {
|
|
|
168
179
|
const useManifest = process.argv.includes("--manifest");
|
|
169
180
|
await connectService(input[1], {
|
|
170
181
|
useManifest,
|
|
182
|
+
save: process.argv.includes("--save"),
|
|
171
183
|
force: flags.force,
|
|
172
184
|
connectedUrls,
|
|
173
185
|
uiUrl: UI_URL,
|
|
174
186
|
});
|
|
175
|
-
|
|
187
|
+
flushAndExit(0);
|
|
176
188
|
} else if (command === "disconnect") {
|
|
177
189
|
await manifestCommands.disconnect(input[1], input[2], {
|
|
178
190
|
connectedUrls,
|
|
179
191
|
uiUrl: UI_URL,
|
|
180
192
|
});
|
|
181
|
-
|
|
193
|
+
flushAndExit(0);
|
|
182
194
|
} else if (command === "manifest") {
|
|
183
195
|
const sub = input[1];
|
|
184
196
|
if (sub === "save") {
|
|
@@ -188,7 +200,7 @@ async function quitApp() {
|
|
|
188
200
|
} else {
|
|
189
201
|
log.warn("Usage: systemview manifest <save|clean>");
|
|
190
202
|
}
|
|
191
|
-
|
|
203
|
+
flushAndExit(0);
|
|
192
204
|
} else if (command === "probe") {
|
|
193
205
|
await launchApp(DEFAULT_PORT);
|
|
194
206
|
const exitCode = await probe(input[1], input[2], {
|
|
@@ -197,7 +209,7 @@ async function quitApp() {
|
|
|
197
209
|
headers: flags.headers,
|
|
198
210
|
uiUrl: UI_URL,
|
|
199
211
|
});
|
|
200
|
-
|
|
212
|
+
flushAndExit(exitCode || 0);
|
|
201
213
|
} else if (command === "logs" || command === "log") {
|
|
202
214
|
await launchApp(DEFAULT_PORT);
|
|
203
215
|
await logsCommand(input[1], input[2], {
|
|
@@ -216,7 +228,7 @@ async function quitApp() {
|
|
|
216
228
|
saved: flags.saved,
|
|
217
229
|
saveLimit: flags.saveLimit,
|
|
218
230
|
});
|
|
219
|
-
if (!flags.follow)
|
|
231
|
+
if (!flags.follow) flushAndExit(0);
|
|
220
232
|
} else {
|
|
221
233
|
await startApp();
|
|
222
234
|
}
|
package/cli/manifest.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { HttpClient, createClient } = require("systemlynx");
|
|
4
4
|
const { createCookieHttpClient } = require("./cookieClient");
|
|
5
|
+
const { getHeaders } = require("./manifestHeaders");
|
|
5
6
|
const cookieHttpClient = createCookieHttpClient();
|
|
6
7
|
const Client = createClient(cookieHttpClient);
|
|
7
8
|
const log = require("./logger");
|
|
@@ -17,7 +18,7 @@ async function getUiSvc(uiUrl) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
module.exports.save = function saveManifest(manifestServices, manifestFile = DEFAULT_MANIFEST
|
|
21
|
+
module.exports.save = function saveManifest(manifestServices, manifestFile = DEFAULT_MANIFEST) {
|
|
21
22
|
if (!manifestServices || !manifestServices.length) {
|
|
22
23
|
log.warn("No services in session to save.");
|
|
23
24
|
return;
|
|
@@ -32,13 +33,19 @@ module.exports.save = function saveManifest(manifestServices, manifestFile = DEF
|
|
|
32
33
|
log.warn("Multiple project codes in session — saving first project only: " + projectCodes[0]);
|
|
33
34
|
}
|
|
34
35
|
const projectCode = projectCodes[0];
|
|
36
|
+
const headers = getHeaders();
|
|
35
37
|
const manifest = {
|
|
36
38
|
projectCode,
|
|
37
39
|
services: byProject[projectCode],
|
|
38
|
-
...(Object.keys(
|
|
40
|
+
...(headers && Object.keys(headers).length && { headers }),
|
|
39
41
|
};
|
|
40
42
|
fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 2));
|
|
41
|
-
|
|
43
|
+
const headerOrigins = Object.keys(headers || {});
|
|
44
|
+
const cookieOrigins = headerOrigins.filter((o) => headers[o] && headers[o].Cookie);
|
|
45
|
+
const parts = [`${manifest.services.length} service(s)`];
|
|
46
|
+
if (headerOrigins.length) parts.push(`headers for ${headerOrigins.length} origin(s)`);
|
|
47
|
+
if (cookieOrigins.length) parts.push(`cookies for ${cookieOrigins.length} origin(s)`);
|
|
48
|
+
log.success(`Manifest saved to ${manifestFile} — ${parts.join(", ")}`);
|
|
42
49
|
};
|
|
43
50
|
|
|
44
51
|
module.exports.clean = async function cleanManifest(manifestFile = DEFAULT_MANIFEST) {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Manifest-backed request headers (RFC-010 Phase 1).
|
|
2
|
+
//
|
|
3
|
+
// Headers live in the manifest, indexed by URL origin. Each value is either a literal
|
|
4
|
+
// ("Bearer abc") or a file-pointer ("@./token") that keeps the secret out of the manifest.
|
|
5
|
+
// Cookies are just the "Cookie" header — a captured Set-Cookie folds into the same store.
|
|
6
|
+
//
|
|
7
|
+
// Read on load, written only on `save`. This module holds the manifest's `headers` in
|
|
8
|
+
// memory for the life of the CLI process (shared across every command via Node's module
|
|
9
|
+
// cache), so a cookie captured by one request is re-sent by the next, and `save` persists
|
|
10
|
+
// whatever accumulated.
|
|
11
|
+
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const path = require("path");
|
|
14
|
+
|
|
15
|
+
const MANIFEST_FILE = path.join(process.cwd(), "systemview.manifest.json");
|
|
16
|
+
|
|
17
|
+
// { "<origin>": { "Header-Name": "value" | "@file", "Cookie": "a=1; b=2" } }
|
|
18
|
+
// This is the ONLY header store — user-authored, indexed by service URL origin. The plugin never
|
|
19
|
+
// writes headers; there is no separate project-level default (the old `probeHeaders` is gone).
|
|
20
|
+
let headers = null;
|
|
21
|
+
|
|
22
|
+
// One-time, de-duplicated notices surfaced by CLI commands so the user gets confirmation
|
|
23
|
+
// that something happened ("using headers for X", "captured cookie for X"). Drained via takeNotices().
|
|
24
|
+
const _notified = new Set();
|
|
25
|
+
const _notices = [];
|
|
26
|
+
function note(key, msg) {
|
|
27
|
+
if (_notified.has(key)) return;
|
|
28
|
+
_notified.add(key);
|
|
29
|
+
_notices.push(msg);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function load() {
|
|
33
|
+
if (headers) return headers;
|
|
34
|
+
headers = {};
|
|
35
|
+
try {
|
|
36
|
+
const m = JSON.parse(fs.readFileSync(MANIFEST_FILE, "utf8"));
|
|
37
|
+
if (m && m.headers && typeof m.headers === "object") headers = m.headers;
|
|
38
|
+
} catch {
|
|
39
|
+
/* no manifest / unreadable — start empty */
|
|
40
|
+
}
|
|
41
|
+
return headers;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function originOf(url) {
|
|
45
|
+
try {
|
|
46
|
+
return new URL(url).origin;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// "@path" → file contents (trimmed); anything else returned as-is.
|
|
53
|
+
function deref(value) {
|
|
54
|
+
if (typeof value === "string" && value.startsWith("@")) {
|
|
55
|
+
try {
|
|
56
|
+
return fs.readFileSync(path.resolve(process.cwd(), value.slice(1)), "utf8").trim();
|
|
57
|
+
} catch {
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Resolved header name→value pairs for a request URL, matched by origin.
|
|
65
|
+
function headersFor(url) {
|
|
66
|
+
const origin = originOf(url);
|
|
67
|
+
load();
|
|
68
|
+
const bucket = headers[origin] || {};
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [name, raw] of Object.entries(bucket)) {
|
|
71
|
+
const value = deref(raw);
|
|
72
|
+
if (value != null && value !== "") out[name] = value;
|
|
73
|
+
}
|
|
74
|
+
const names = Object.keys(out);
|
|
75
|
+
if (names.length) note("attach:" + origin, `using ${names.length} manifest header(s) for ${origin}`);
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fold Set-Cookie response headers into the in-memory Cookie header for the url's origin.
|
|
80
|
+
function captureCookie(url, setCookieHeaders) {
|
|
81
|
+
if (!setCookieHeaders || !setCookieHeaders.length) return;
|
|
82
|
+
const origin = originOf(url);
|
|
83
|
+
if (!origin) return;
|
|
84
|
+
const store = load();
|
|
85
|
+
if (!store[origin]) store[origin] = {};
|
|
86
|
+
|
|
87
|
+
const jar = {};
|
|
88
|
+
const existing = store[origin].Cookie;
|
|
89
|
+
if (typeof existing === "string") {
|
|
90
|
+
existing.split(";").forEach((p) => {
|
|
91
|
+
const i = p.indexOf("=");
|
|
92
|
+
if (i > -1) jar[p.slice(0, i).trim()] = p.slice(i + 1).trim();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
setCookieHeaders.forEach((h) => {
|
|
96
|
+
const [pair] = h.split(";");
|
|
97
|
+
const i = pair.indexOf("=");
|
|
98
|
+
if (i > -1) jar[pair.slice(0, i).trim()] = pair.slice(i + 1).trim();
|
|
99
|
+
});
|
|
100
|
+
store[origin].Cookie = Object.entries(jar)
|
|
101
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
102
|
+
.join("; ");
|
|
103
|
+
note("cookie:" + origin, `captured cookie for ${origin}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The live headers map — for `save` to persist. (Not dereferenced: @file pointers and
|
|
107
|
+
// literals are written back as authored; captured cookies are written as literal values.)
|
|
108
|
+
function getHeaders() {
|
|
109
|
+
return load();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Drain the accumulated one-time notices (for a command to print as confirmations).
|
|
113
|
+
function takeNotices() {
|
|
114
|
+
return _notices.splice(0, _notices.length);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { headersFor, captureCookie, getHeaders, takeNotices, MANIFEST_FILE };
|
package/cli/probe.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { createClient } = require("systemlynx");
|
|
4
4
|
const { createCookieHttpClient } = require("./cookieClient");
|
|
5
|
+
const { takeNotices } = require("./manifestHeaders");
|
|
5
6
|
const log = require("./logger");
|
|
6
7
|
|
|
7
8
|
const cookieHttpClient = createCookieHttpClient();
|
|
@@ -76,6 +77,7 @@ module.exports = async function probe(namespace, argsStr, { json = false, manife
|
|
|
76
77
|
} else {
|
|
77
78
|
log.success("result:");
|
|
78
79
|
console.log(JSON.stringify(result, null, 2));
|
|
80
|
+
takeNotices().forEach((n) => log.info(n));
|
|
79
81
|
}
|
|
80
82
|
return 0;
|
|
81
83
|
} catch (err) {
|
|
@@ -83,6 +85,7 @@ module.exports = async function probe(namespace, argsStr, { json = false, manife
|
|
|
83
85
|
process.stdout.write(JSON.stringify({ serviceId, moduleName, methodName, args, error: err.message }, null, 2) + "\n");
|
|
84
86
|
} else {
|
|
85
87
|
log.error(err.message);
|
|
88
|
+
takeNotices().forEach((n) => log.info(n));
|
|
86
89
|
}
|
|
87
90
|
return 1;
|
|
88
91
|
}
|
package/cli/utils/cli.js
CHANGED
|
@@ -13,7 +13,9 @@ const HELP_TEXT = `
|
|
|
13
13
|
flush Wipe the log file and stop streaming [alias: clearlogs]
|
|
14
14
|
connect [url|projectCode] Connect a service URL, reconnect a stored project, or connect all
|
|
15
15
|
connect <url> --manifest Connect via plugin manifest — registers under real projectCode
|
|
16
|
+
connect <url> --manifest --save Connect, then persist the connection (services + headers + cookie) to the manifest
|
|
16
17
|
disconnect [projectCode] [serviceId] Remove a project or service from the UI store
|
|
18
|
+
manifest save Persist session manifest — services, auth headers, cookies [interactive]
|
|
17
19
|
manifest clean Re-probe manifest entries, remove stale ones
|
|
18
20
|
probe <ServiceId.Module.method> [args] Call a service method ad-hoc
|
|
19
21
|
open [projectCode] [namespace] Open the browser UI
|
|
@@ -26,7 +28,9 @@ const HELP_TEXT = `
|
|
|
26
28
|
--verbose test: full results and args for all phases; list: expand hierarchy
|
|
27
29
|
--manifest connect: use plugin manifest to get real projectCode
|
|
28
30
|
--manifest <path> probe: path to manifest file (default: ./systemview.manifest.json)
|
|
29
|
-
--header "Name: Value" Extra request header (repeatable)
|
|
31
|
+
--header "Name: Value" Extra request header (repeatable; overrides manifest headers).
|
|
32
|
+
For a standing token, add it to the manifest "headers"
|
|
33
|
+
(literal or "@file") — see docs/cli.md.
|
|
30
34
|
--skip <pattern> Exclude tests matching pattern (repeatable)
|
|
31
35
|
--bail Stop after first failure
|
|
32
36
|
--dry-run Print which tests would run without executing
|
|
@@ -42,6 +46,7 @@ const HELP_TEXT = `
|
|
|
42
46
|
--filter missing=<field> → only entries where field is absent
|
|
43
47
|
--or <field=value> logs: OR filter on a field (repeatable)
|
|
44
48
|
--include <field> logs: include extra field as a column (repeatable)
|
|
49
|
+
--save connect: persist this connection to the manifest (headers + cookie tag along)
|
|
45
50
|
--save [path] logs: append streamed entries to a local snapshot file
|
|
46
51
|
--saved logs: read from local snapshot instead of live service
|
|
47
52
|
--save-limit <n> logs: max entries to keep in snapshot (default 500)
|
package/package.json
CHANGED
|
File without changes
|