@pyxmate/memory 1.27.0 → 1.27.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/dist/chunk-4DXEQC6X.mjs +16 -0
- package/dist/chunk-7IGU3HJO.mjs +70 -0
- package/dist/chunk-7TATCSSN.mjs +650 -0
- package/dist/chunk-QR5K7ETI.mjs +793 -0
- package/dist/chunk-S3CX323Z.mjs +34 -0
- package/dist/chunk-THLUBOGD.mjs +263 -0
- package/dist/chunk-UESIQ47D.mjs +165 -0
- package/dist/chunk-YBPODKB5.mjs +132 -0
- package/dist/cli/pyx-mem.mjs +63 -4226
- package/dist/doctor-AERNUHEZ.mjs +143 -0
- package/dist/insights-PD7Y6QDX.mjs +139 -0
- package/dist/login-4ALCAN4M.mjs +179 -0
- package/dist/login-browser-DO2O2Q2G.mjs +407 -0
- package/dist/login-recovery-AOO76PYX.mjs +97 -0
- package/dist/logout-4CO53LNA.mjs +13 -0
- package/dist/mcp-ER3A45IY.mjs +944 -0
- package/dist/mcp-install-KBB45OB2.mjs +36 -0
- package/dist/overview-COBXHLP3.mjs +88 -0
- package/dist/scaffold-2FSCWF22.mjs +186 -0
- package/dist/status-UHJZLEPL.mjs +74 -0
- package/dist/taxonomy-EKDFDFXW.mjs +103 -0
- package/package.json +1 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
probeCredentials
|
|
3
|
+
} from "./chunk-S3CX323Z.mjs";
|
|
4
|
+
import {
|
|
5
|
+
KeychainError,
|
|
6
|
+
getDefaultKeychain
|
|
7
|
+
} from "./chunk-7TATCSSN.mjs";
|
|
8
|
+
import {
|
|
9
|
+
EXIT
|
|
10
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
11
|
+
|
|
12
|
+
// src/cli/commands/doctor.ts
|
|
13
|
+
async function doctorCommand(opts = {}) {
|
|
14
|
+
const provider = opts.keychain ?? getDefaultKeychain();
|
|
15
|
+
const fetcher = opts.fetchImpl ?? fetch;
|
|
16
|
+
const checks = [];
|
|
17
|
+
const accessCheck = await provider.check();
|
|
18
|
+
if (accessCheck.ok) {
|
|
19
|
+
checks.push({ id: "provider.import", status: "pass", message: "native keyring module loaded" });
|
|
20
|
+
checks.push({ id: "keychain.access", status: "pass", message: accessCheck.message });
|
|
21
|
+
} else if (accessCheck.code === "provider-import-failed") {
|
|
22
|
+
checks.push({
|
|
23
|
+
id: "provider.import",
|
|
24
|
+
status: "fail",
|
|
25
|
+
code: accessCheck.code,
|
|
26
|
+
message: accessCheck.message,
|
|
27
|
+
guidance: accessCheck.guidance
|
|
28
|
+
});
|
|
29
|
+
checks.push({
|
|
30
|
+
id: "keychain.access",
|
|
31
|
+
status: "skip",
|
|
32
|
+
message: "skipped \u2014 provider import failed"
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
checks.push({ id: "provider.import", status: "pass", message: "native keyring module loaded" });
|
|
36
|
+
checks.push({
|
|
37
|
+
id: "keychain.access",
|
|
38
|
+
status: "fail",
|
|
39
|
+
code: accessCheck.code,
|
|
40
|
+
message: accessCheck.message,
|
|
41
|
+
guidance: accessCheck.guidance
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const accessOk = accessCheck.ok;
|
|
45
|
+
let creds = null;
|
|
46
|
+
if (accessOk) {
|
|
47
|
+
try {
|
|
48
|
+
creds = await provider.read();
|
|
49
|
+
if (creds) {
|
|
50
|
+
checks.push({
|
|
51
|
+
id: "credentials.present",
|
|
52
|
+
status: "pass",
|
|
53
|
+
message: "credentials present in keychain",
|
|
54
|
+
endpoint: creds.endpoint
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
checks.push({
|
|
58
|
+
id: "credentials.present",
|
|
59
|
+
status: "fail",
|
|
60
|
+
code: "not-logged-in",
|
|
61
|
+
message: "no pyx-memory credentials stored",
|
|
62
|
+
guidance: "Run: pyx-mem login"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (err instanceof KeychainError) {
|
|
67
|
+
checks.push({
|
|
68
|
+
id: "credentials.present",
|
|
69
|
+
status: "fail",
|
|
70
|
+
code: err.code,
|
|
71
|
+
message: err.message,
|
|
72
|
+
guidance: err.guidance
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
checks.push({
|
|
80
|
+
id: "credentials.present",
|
|
81
|
+
status: "skip",
|
|
82
|
+
message: "skipped \u2014 keychain access failed"
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (creds) {
|
|
86
|
+
const probe = await probeCredentials({
|
|
87
|
+
endpoint: creds.endpoint,
|
|
88
|
+
apiKey: creds.apiKey,
|
|
89
|
+
fetcher
|
|
90
|
+
});
|
|
91
|
+
if (probe.kind === "ok") {
|
|
92
|
+
checks.push({
|
|
93
|
+
id: "backend.reachable",
|
|
94
|
+
status: "pass",
|
|
95
|
+
message: `pyx-memory API reachable (HTTP ${probe.status})`,
|
|
96
|
+
endpoint: creds.endpoint
|
|
97
|
+
});
|
|
98
|
+
} else if (probe.kind === "invalid") {
|
|
99
|
+
checks.push({
|
|
100
|
+
id: "backend.reachable",
|
|
101
|
+
status: "fail",
|
|
102
|
+
code: "key-rejected",
|
|
103
|
+
message: `API key rejected (HTTP ${probe.status})`,
|
|
104
|
+
endpoint: creds.endpoint,
|
|
105
|
+
guidance: "Re-login with a valid key: pyx-mem login"
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
checks.push({
|
|
109
|
+
id: "backend.reachable",
|
|
110
|
+
status: "fail",
|
|
111
|
+
code: "unreachable",
|
|
112
|
+
message: `no pyx-memory API at this endpoint (${probe.reason})`,
|
|
113
|
+
endpoint: creds.endpoint,
|
|
114
|
+
guidance: "Verify the endpoint is the API backend, not a web UI: pyx-mem login --endpoint <API_URL>"
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
checks.push({
|
|
119
|
+
id: "backend.reachable",
|
|
120
|
+
status: "skip",
|
|
121
|
+
message: "skipped \u2014 no credentials to probe"
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const ok = checks.every((c) => c.status !== "fail");
|
|
125
|
+
if (opts.json) {
|
|
126
|
+
process.stdout.write(`${JSON.stringify({ ok, checks })}
|
|
127
|
+
`);
|
|
128
|
+
} else {
|
|
129
|
+
for (const c of checks) {
|
|
130
|
+
const tag = c.status === "pass" ? "OK " : c.status === "skip" ? "SKIP" : "FAIL";
|
|
131
|
+
process.stdout.write(`[${tag}] ${c.id}: ${c.message}
|
|
132
|
+
`);
|
|
133
|
+
if (c.endpoint) process.stdout.write(` endpoint: ${c.endpoint}
|
|
134
|
+
`);
|
|
135
|
+
if (c.status === "fail" && c.guidance) process.stdout.write(` ${c.guidance}
|
|
136
|
+
`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ok ? EXIT.OK : EXIT.DOCTOR_FAIL;
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
doctorCommand
|
|
143
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readSurface
|
|
3
|
+
} from "./chunk-UESIQ47D.mjs";
|
|
4
|
+
import "./chunk-THLUBOGD.mjs";
|
|
5
|
+
import "./chunk-7TATCSSN.mjs";
|
|
6
|
+
import {
|
|
7
|
+
MEMORY_PROJECT_LABEL_MAX_CHARS,
|
|
8
|
+
TAXONOMY_MAX_PROJECTS,
|
|
9
|
+
normalizeMemoryActorLabel
|
|
10
|
+
} from "./chunk-RWQTBXUC.mjs";
|
|
11
|
+
import "./chunk-HTQBKJTD.mjs";
|
|
12
|
+
import {
|
|
13
|
+
EXIT
|
|
14
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
15
|
+
|
|
16
|
+
// src/cli/commands/insights.ts
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
var INSIGHTS_SCHEMA_VERSION = 1;
|
|
19
|
+
var metricsSchema = z.object({
|
|
20
|
+
activeEntries: z.number().int().nonnegative(),
|
|
21
|
+
retainedAdditions7d: z.number().int().nonnegative(),
|
|
22
|
+
appliedRecalls: z.number().int().nonnegative(),
|
|
23
|
+
reinforcedEntries: z.number().int().nonnegative(),
|
|
24
|
+
retrievedEntries: z.number().int().nonnegative(),
|
|
25
|
+
retrievals: z.number().int().nonnegative(),
|
|
26
|
+
lastAppliedAt: z.string().nullable(),
|
|
27
|
+
lastRetrievedAt: z.string().nullable()
|
|
28
|
+
});
|
|
29
|
+
var insightsSchema = z.object({
|
|
30
|
+
generatedAt: z.string(),
|
|
31
|
+
scope: z.literal("connected_memory"),
|
|
32
|
+
coverage: z.object({
|
|
33
|
+
retainedAdditions: z.object({
|
|
34
|
+
state: z.enum(["measured", "partial"]),
|
|
35
|
+
trackingSince: z.string().nullable(),
|
|
36
|
+
trackedEntries: z.number().int().nonnegative(),
|
|
37
|
+
totalEntries: z.number().int().nonnegative(),
|
|
38
|
+
reason: z.string().nullable()
|
|
39
|
+
}),
|
|
40
|
+
projectLabels: z.object({ state: z.literal("measured") }),
|
|
41
|
+
retrievalUsage: z.object({ state: z.literal("partial"), reason: z.string() }),
|
|
42
|
+
tokenEfficiency: z.object({ state: z.literal("not_instrumented"), reason: z.string() }),
|
|
43
|
+
contextAccuracy: z.object({ state: z.literal("not_instrumented"), reason: z.string() })
|
|
44
|
+
}),
|
|
45
|
+
summary: z.object({
|
|
46
|
+
activeEntries: z.number().int().nonnegative(),
|
|
47
|
+
observedProjectLabels: z.number().int().nonnegative(),
|
|
48
|
+
retainedAdditions7d: z.number().int().nonnegative().nullable(),
|
|
49
|
+
trackedEntries: z.number().int().nonnegative(),
|
|
50
|
+
appliedRecalls: z.number().int().nonnegative(),
|
|
51
|
+
reinforcedEntries: z.number().int().nonnegative(),
|
|
52
|
+
retrievedEntries: z.number().int().nonnegative(),
|
|
53
|
+
retrievals: z.number().int().nonnegative()
|
|
54
|
+
}),
|
|
55
|
+
projects: z.array(
|
|
56
|
+
metricsSchema.extend({
|
|
57
|
+
label: z.string().min(1).max(MEMORY_PROJECT_LABEL_MAX_CHARS)
|
|
58
|
+
})
|
|
59
|
+
).max(TAXONOMY_MAX_PROJECTS),
|
|
60
|
+
other: metricsSchema.nullable(),
|
|
61
|
+
recentApplied: z.array(
|
|
62
|
+
z.object({
|
|
63
|
+
topic: z.string().min(1).max(MEMORY_PROJECT_LABEL_MAX_CHARS).nullable(),
|
|
64
|
+
project: z.string().min(1).max(MEMORY_PROJECT_LABEL_MAX_CHARS).nullable(),
|
|
65
|
+
at: z.string()
|
|
66
|
+
})
|
|
67
|
+
).max(5)
|
|
68
|
+
});
|
|
69
|
+
var actorSchema = z.string().refine((value) => normalizeMemoryActorLabel(value) === value).nullable().default(null);
|
|
70
|
+
var insightsWithProvenanceSchema = insightsSchema.extend({
|
|
71
|
+
recentApplied: z.array(
|
|
72
|
+
insightsSchema.shape.recentApplied.element.extend({
|
|
73
|
+
writtenBy: actorSchema,
|
|
74
|
+
writtenAt: z.iso.datetime({ offset: true }).nullable().default(null),
|
|
75
|
+
appliedBy: actorSchema
|
|
76
|
+
})
|
|
77
|
+
).max(5)
|
|
78
|
+
});
|
|
79
|
+
async function insightsCommand(opts = {}) {
|
|
80
|
+
const schema = opts.withProvenance === true ? insightsWithProvenanceSchema : insightsSchema;
|
|
81
|
+
const result = await readSurface({
|
|
82
|
+
surface: "insights",
|
|
83
|
+
request: { method: "GET", path: "/api/memory/insights" },
|
|
84
|
+
schema: z.union([
|
|
85
|
+
schema,
|
|
86
|
+
z.object({ success: z.literal(true), data: schema }).transform(({ data }) => data)
|
|
87
|
+
]),
|
|
88
|
+
minimumServiceVersion: "1.19.0",
|
|
89
|
+
keychain: opts.keychain,
|
|
90
|
+
fetchImpl: opts.fetchImpl
|
|
91
|
+
});
|
|
92
|
+
const withIdentity = opts.withIdentity === true;
|
|
93
|
+
if (!result.ok) {
|
|
94
|
+
emit(
|
|
95
|
+
report(result.state, result.endpoint, result.keySource, result.problem, {
|
|
96
|
+
withIdentity,
|
|
97
|
+
credentialFingerprint: result.credentialFingerprint
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
return result.exit;
|
|
101
|
+
}
|
|
102
|
+
emit({
|
|
103
|
+
schemaVersion: INSIGHTS_SCHEMA_VERSION,
|
|
104
|
+
state: "ready",
|
|
105
|
+
endpoint: result.endpoint,
|
|
106
|
+
keySource: "keychain",
|
|
107
|
+
...identityField({ withIdentity, credentialFingerprint: result.credentialFingerprint }),
|
|
108
|
+
...result.data,
|
|
109
|
+
problem: null
|
|
110
|
+
});
|
|
111
|
+
return EXIT.OK;
|
|
112
|
+
}
|
|
113
|
+
function identityField(identity) {
|
|
114
|
+
return identity.withIdentity ? { credentialFingerprint: identity.credentialFingerprint } : {};
|
|
115
|
+
}
|
|
116
|
+
function report(state, endpoint, keySource, problem, identity) {
|
|
117
|
+
return {
|
|
118
|
+
schemaVersion: INSIGHTS_SCHEMA_VERSION,
|
|
119
|
+
state,
|
|
120
|
+
endpoint,
|
|
121
|
+
keySource,
|
|
122
|
+
...identityField(identity),
|
|
123
|
+
generatedAt: null,
|
|
124
|
+
scope: "connected_memory",
|
|
125
|
+
coverage: null,
|
|
126
|
+
summary: null,
|
|
127
|
+
projects: [],
|
|
128
|
+
other: null,
|
|
129
|
+
recentApplied: [],
|
|
130
|
+
problem
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function emit(value) {
|
|
134
|
+
process.stdout.write(`${JSON.stringify(value)}
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
insightsCommand
|
|
139
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
probeCredentials
|
|
3
|
+
} from "./chunk-S3CX323Z.mjs";
|
|
4
|
+
import {
|
|
5
|
+
BUSY,
|
|
6
|
+
KeychainError,
|
|
7
|
+
getLifecycleKeychain,
|
|
8
|
+
supersedeLogout,
|
|
9
|
+
withLifecycleLock
|
|
10
|
+
} from "./chunk-7TATCSSN.mjs";
|
|
11
|
+
import {
|
|
12
|
+
EXIT
|
|
13
|
+
} from "./chunk-4DXEQC6X.mjs";
|
|
14
|
+
|
|
15
|
+
// src/cli/prompt.ts
|
|
16
|
+
async function readApiKeySecret() {
|
|
17
|
+
if (process.stdin.isTTY) {
|
|
18
|
+
return promptMasked("API key: ");
|
|
19
|
+
}
|
|
20
|
+
return readSingleLineFromStdin();
|
|
21
|
+
}
|
|
22
|
+
async function promptMasked(label) {
|
|
23
|
+
const { stdin, stdout } = process;
|
|
24
|
+
stdout.write(label);
|
|
25
|
+
const hadRawMode = typeof stdin.setRawMode === "function";
|
|
26
|
+
if (!hadRawMode) {
|
|
27
|
+
const value = await readSingleLineFromStdin();
|
|
28
|
+
stdout.write(`${"\u2022".repeat(Math.min(value.length, 80))}
|
|
29
|
+
`);
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const previousEncoding = stdin.readableEncoding;
|
|
34
|
+
stdin.setEncoding("utf8");
|
|
35
|
+
stdin.setRawMode(true);
|
|
36
|
+
stdin.resume();
|
|
37
|
+
let buf = "";
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
stdin.removeListener("data", onData);
|
|
40
|
+
stdin.removeListener("error", onError);
|
|
41
|
+
stdin.setRawMode(false);
|
|
42
|
+
stdin.pause();
|
|
43
|
+
if (previousEncoding) stdin.setEncoding(previousEncoding);
|
|
44
|
+
};
|
|
45
|
+
const onData = (chunk) => {
|
|
46
|
+
for (const ch of chunk) {
|
|
47
|
+
if (ch === "\n" || ch === "\r") {
|
|
48
|
+
cleanup();
|
|
49
|
+
stdout.write("\n");
|
|
50
|
+
resolve(buf.trim());
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (ch === "") {
|
|
54
|
+
cleanup();
|
|
55
|
+
stdout.write("\n");
|
|
56
|
+
process.exit(130);
|
|
57
|
+
}
|
|
58
|
+
if (ch === "\x7F" || ch === "\b") {
|
|
59
|
+
if (buf.length > 0) {
|
|
60
|
+
buf = buf.slice(0, -1);
|
|
61
|
+
stdout.write("\b \b");
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (ch < " ") {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
buf += ch;
|
|
69
|
+
stdout.write("\u2022");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const onError = (err) => {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
75
|
+
};
|
|
76
|
+
stdin.on("data", onData);
|
|
77
|
+
stdin.on("error", onError);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function readSingleLineFromStdin() {
|
|
81
|
+
let buf = "";
|
|
82
|
+
for await (const chunk of process.stdin) {
|
|
83
|
+
buf += chunk.toString("utf8");
|
|
84
|
+
const nl = buf.indexOf("\n");
|
|
85
|
+
if (nl >= 0) return buf.slice(0, nl).trim();
|
|
86
|
+
}
|
|
87
|
+
return buf.trim();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/cli/commands/login.ts
|
|
91
|
+
var DEFAULT_ENDPOINT = "https://memory.api.pyxmate.com";
|
|
92
|
+
async function loginCommand(opts = {}) {
|
|
93
|
+
const provider = opts.keychain ?? getLifecycleKeychain();
|
|
94
|
+
const fetcher = opts.fetchImpl ?? fetch;
|
|
95
|
+
const endpoint = normalizeEndpoint(
|
|
96
|
+
opts.endpoint ?? await readExistingEndpoint(provider) ?? DEFAULT_ENDPOINT
|
|
97
|
+
);
|
|
98
|
+
if (!isHttpUrl(endpoint)) {
|
|
99
|
+
process.stderr.write(`Error: invalid --endpoint \`${endpoint}\` (expected http(s) URL).
|
|
100
|
+
`);
|
|
101
|
+
return EXIT.USAGE;
|
|
102
|
+
}
|
|
103
|
+
let apiKey;
|
|
104
|
+
try {
|
|
105
|
+
apiKey = opts.apiKey?.trim() || await readApiKeySecret();
|
|
106
|
+
} catch (err) {
|
|
107
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
108
|
+
`);
|
|
109
|
+
return EXIT.USAGE;
|
|
110
|
+
}
|
|
111
|
+
if (!apiKey) {
|
|
112
|
+
process.stderr.write("Error: API key is empty.\n");
|
|
113
|
+
return EXIT.USAGE;
|
|
114
|
+
}
|
|
115
|
+
const probe = await probeCredentials({ endpoint, apiKey, fetcher });
|
|
116
|
+
if (probe.kind === "invalid") {
|
|
117
|
+
process.stderr.write(
|
|
118
|
+
`Error: API key was rejected by ${endpoint} (HTTP ${probe.status}).
|
|
119
|
+
No credentials were written.
|
|
120
|
+
`
|
|
121
|
+
);
|
|
122
|
+
return EXIT.NOT_LOGGED_IN;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const written = await withLifecycleLock(provider, async () => {
|
|
126
|
+
await supersedeLogout(provider, endpoint, apiKey);
|
|
127
|
+
await provider.write({ schemaVersion: 1, endpoint, apiKey });
|
|
128
|
+
});
|
|
129
|
+
if (written === BUSY) {
|
|
130
|
+
process.stderr.write(
|
|
131
|
+
"Error: another pyx-mem sign-in or sign-out is in progress. Retry in a moment.\nNo credentials were written.\n"
|
|
132
|
+
);
|
|
133
|
+
return EXIT.BUSY;
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
if (err instanceof KeychainError) {
|
|
137
|
+
process.stderr.write(
|
|
138
|
+
`Error: ${err.message}
|
|
139
|
+
${err.guidance}
|
|
140
|
+
No credentials were written. pyx-mem does not write plaintext token files.
|
|
141
|
+
`
|
|
142
|
+
);
|
|
143
|
+
return err.exit;
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
if (probe.kind === "unreachable") {
|
|
148
|
+
process.stdout.write(
|
|
149
|
+
`Stored credentials for ${endpoint}. Warning: validation probe failed (${probe.reason}). Run: pyx-mem doctor
|
|
150
|
+
`
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
process.stdout.write(`Logged in to ${endpoint}. Run: pyx-mem doctor
|
|
154
|
+
`);
|
|
155
|
+
}
|
|
156
|
+
return EXIT.OK;
|
|
157
|
+
}
|
|
158
|
+
async function readExistingEndpoint(provider) {
|
|
159
|
+
try {
|
|
160
|
+
const creds = await provider.read();
|
|
161
|
+
return creds?.endpoint ?? null;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function normalizeEndpoint(input) {
|
|
167
|
+
return input.replace(/\/+$/, "");
|
|
168
|
+
}
|
|
169
|
+
function isHttpUrl(value) {
|
|
170
|
+
try {
|
|
171
|
+
const u = new URL(value);
|
|
172
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
173
|
+
} catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
loginCommand
|
|
179
|
+
};
|