umbrella-context 0.1.37 → 0.1.39
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 +9 -2
- package/dist/adapters/umbrella-onboarding.js +3 -2
- package/dist/commands/connect.js +3 -2
- package/dist/commands/connectors.js +3 -0
- package/dist/commands/curate.d.ts +13 -1
- package/dist/commands/curate.js +146 -10
- package/dist/commands/fix.js +2 -10
- package/dist/commands/hub.d.ts +3 -1
- package/dist/commands/hub.js +218 -68
- package/dist/commands/interactive.js +21 -10
- package/dist/commands/locations.d.ts +6 -1
- package/dist/commands/locations.js +91 -5
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/pull.d.ts +6 -1
- package/dist/commands/pull.js +119 -3
- package/dist/commands/push.d.ts +6 -1
- package/dist/commands/push.js +147 -3
- package/dist/commands/restart.js +6 -1
- package/dist/commands/search.d.ts +5 -1
- package/dist/commands/search.js +857 -36
- package/dist/commands/session.js +0 -3
- package/dist/commands/setup.js +186 -26
- package/dist/commands/space.js +4 -3
- package/dist/commands/status.d.ts +16 -1
- package/dist/commands/status.js +111 -47
- package/dist/commands/tui.js +339 -107
- package/dist/config.d.ts +4 -0
- package/dist/config.js +6 -0
- package/dist/index.js +21 -3
- package/dist/repo-state.d.ts +115 -0
- package/dist/repo-state.js +195 -12
- package/package.json +2 -2
package/dist/commands/session.js
CHANGED
|
@@ -226,9 +226,6 @@ export function sessionCommand(cli) {
|
|
|
226
226
|
cli.command("recent", "Show saved recent repo activity").action(async () => {
|
|
227
227
|
await sessionCommandAction("recent");
|
|
228
228
|
});
|
|
229
|
-
cli.command("activity", "Show the saved richer repo activity feed").action(async () => {
|
|
230
|
-
await sessionCommandAction("activity");
|
|
231
|
-
});
|
|
232
229
|
cli.command("timeline", "Show the saved session event timeline").action(async () => {
|
|
233
230
|
await sessionCommandAction("timeline");
|
|
234
231
|
});
|
package/dist/commands/setup.js
CHANGED
|
@@ -6,6 +6,7 @@ import { UmbrellaRequestError } from "../umbrella.js";
|
|
|
6
6
|
import { setUmbrellaAuthAuthorized, setUmbrellaAuthChecking, setUmbrellaAuthFailure, } from "../adapters/umbrella-auth-runtime.js";
|
|
7
7
|
import { checkUmbrellaOnboardingServer, connectUmbrellaDevice, createUmbrellaOnboardingCompany, createUmbrellaOnboardingSpace, loadUmbrellaOnboardingSpaces, normalizeUmbrellaServerUrl, signInUmbrellaOnboarding, } from "../adapters/umbrella-onboarding.js";
|
|
8
8
|
const DEFAULT_UMBRELLA_SERVER_URL = "http://5.161.55.138:3100";
|
|
9
|
+
const LOCAL_UMBRELLA_SERVER_URL = "http://127.0.0.1:3100";
|
|
9
10
|
function isLocalOnlyServerUrl(value) {
|
|
10
11
|
try {
|
|
11
12
|
const parsed = new URL(value);
|
|
@@ -16,6 +17,29 @@ function isLocalOnlyServerUrl(value) {
|
|
|
16
17
|
return false;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
function detectMixedSavedConnection(umbrellaUrl, serverUrl) {
|
|
21
|
+
if (!umbrellaUrl || !serverUrl)
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const umbrella = new URL(umbrellaUrl);
|
|
25
|
+
const backend = new URL(serverUrl);
|
|
26
|
+
const umbrellaIsLoopback = isLocalOnlyServerUrl(umbrellaUrl);
|
|
27
|
+
const backendIsLoopback = isLocalOnlyServerUrl(serverUrl);
|
|
28
|
+
const differentHosts = umbrella.hostname !== backend.hostname || umbrella.port !== backend.port;
|
|
29
|
+
if (!differentHosts && umbrellaIsLoopback === backendIsLoopback) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
backendUrl: serverUrl,
|
|
34
|
+
umbrellaUrl,
|
|
35
|
+
umbrellaIsLoopback,
|
|
36
|
+
backendIsLoopback,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
19
43
|
function printConnected(setup) {
|
|
20
44
|
console.log(chalk.green(`\n Connected to ${setup.companyName} / ${setup.activeSpaceName}`));
|
|
21
45
|
console.log(chalk.gray(` Context space: ${setup.activeSpaceName}`));
|
|
@@ -63,6 +87,69 @@ function describeSetupFailure(error) {
|
|
|
63
87
|
}
|
|
64
88
|
return error.message;
|
|
65
89
|
}
|
|
90
|
+
async function promptForCredentials(options) {
|
|
91
|
+
const emailPrompt = await prompts({
|
|
92
|
+
message: "Umbrella email",
|
|
93
|
+
name: "value",
|
|
94
|
+
type: options.email ? null : "text",
|
|
95
|
+
});
|
|
96
|
+
const passwordPrompt = await prompts({
|
|
97
|
+
message: "Umbrella password",
|
|
98
|
+
name: "value",
|
|
99
|
+
type: options.password ? null : "password",
|
|
100
|
+
});
|
|
101
|
+
const email = options.email?.trim() || emailPrompt.value;
|
|
102
|
+
const password = options.password?.trim() || passwordPrompt.value;
|
|
103
|
+
if (!email || !password) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return { email, password };
|
|
107
|
+
}
|
|
108
|
+
async function attemptAuthenticatedSignIn(serverUrl, options) {
|
|
109
|
+
const canRetryInteractively = !options.email && !options.password;
|
|
110
|
+
const maxAttempts = canRetryInteractively ? 3 : 1;
|
|
111
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
112
|
+
const credentials = await promptForCredentials(options);
|
|
113
|
+
if (!credentials) {
|
|
114
|
+
return { kind: "cancelled", error: new Error("Sign-in cancelled.") };
|
|
115
|
+
}
|
|
116
|
+
const { email, password } = credentials;
|
|
117
|
+
try {
|
|
118
|
+
console.log(chalk.gray(` Signing into Umbrella as ${email} ...`));
|
|
119
|
+
const signIn = await signInUmbrellaOnboarding(serverUrl, email, password);
|
|
120
|
+
return {
|
|
121
|
+
companies: signIn.companies,
|
|
122
|
+
cookie: signIn.cookie,
|
|
123
|
+
email,
|
|
124
|
+
kind: "success",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
const message = describeSetupFailure(error);
|
|
129
|
+
console.log(chalk.red(` Sign-in failed: ${message}`));
|
|
130
|
+
if (attempt >= maxAttempts) {
|
|
131
|
+
return { kind: "failure", error };
|
|
132
|
+
}
|
|
133
|
+
const retryPrompt = await prompts({
|
|
134
|
+
choices: [
|
|
135
|
+
{ title: "Try email and password again", value: "retry" },
|
|
136
|
+
{ title: "Cancel setup", value: "cancel" },
|
|
137
|
+
],
|
|
138
|
+
initial: 0,
|
|
139
|
+
message: "The live Umbrella sign-in did not work. What do you want to do?",
|
|
140
|
+
name: "value",
|
|
141
|
+
type: "select",
|
|
142
|
+
});
|
|
143
|
+
if (retryPrompt.value !== "retry") {
|
|
144
|
+
return { kind: "cancelled", error: new Error("Sign-in cancelled.") };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
kind: "failure",
|
|
150
|
+
error: new Error("Sign-in failed."),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
66
153
|
async function chooseCompany(serverUrl, companies, cookie, initialCompanyId) {
|
|
67
154
|
if (initialCompanyId) {
|
|
68
155
|
const matched = companies.find((company) => company.id === initialCompanyId);
|
|
@@ -132,30 +219,108 @@ async function chooseSpace(serverUrl, companyId, spaces, cookie, initialSpaceId)
|
|
|
132
219
|
}
|
|
133
220
|
export function setupCommand(cli) {
|
|
134
221
|
cli
|
|
135
|
-
.command("setup",
|
|
222
|
+
.command("setup", `Sign into Umbrella, choose a company, and connect this device to a context space
|
|
223
|
+
|
|
224
|
+
Use this the first time you connect a machine, when the saved company or space looks wrong, or when status says your live Umbrella connection has not been verified yet.`)
|
|
136
225
|
.option("--server-url <url>", "Umbrella server URL")
|
|
137
226
|
.option("--email <email>", "Umbrella email for authenticated mode")
|
|
138
227
|
.option("--password <password>", "Umbrella password for authenticated mode")
|
|
139
228
|
.option("--company-id <id>", "Existing Umbrella company ID")
|
|
140
229
|
.option("--space-id <id>", "Existing context space ID")
|
|
230
|
+
.example("setup")
|
|
231
|
+
.example("setup --server-url http://5.161.55.138:3100")
|
|
232
|
+
.example("setup --server-url http://127.0.0.1:3100")
|
|
141
233
|
.action(async (options) => {
|
|
142
234
|
console.log(chalk.bold("\n Umbrella Context Setup\n"));
|
|
143
235
|
const existing = configManager.config;
|
|
144
236
|
const envServerUrl = process.env.UMBRELLA_SERVER_URL?.trim();
|
|
237
|
+
const mixedSavedConnection = detectMixedSavedConnection(existing?.umbrellaUrl, existing?.serverUrl);
|
|
145
238
|
const suggestedServerUrl = options.serverUrl?.trim() ||
|
|
146
239
|
envServerUrl ||
|
|
240
|
+
mixedSavedConnection?.backendUrl ||
|
|
147
241
|
existing?.umbrellaUrl ||
|
|
148
242
|
existing?.serverUrl ||
|
|
149
243
|
DEFAULT_UMBRELLA_SERVER_URL;
|
|
150
244
|
console.log(chalk.gray(` Defaulting to the live Umbrella server at ${DEFAULT_UMBRELLA_SERVER_URL}. Only use 127.0.0.1 if you are intentionally running Umbrella on this same computer instead.`));
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
245
|
+
if (mixedSavedConnection) {
|
|
246
|
+
console.log(chalk.yellow(` Saved setup is mixed right now: Umbrella app URL is ${mixedSavedConnection.umbrellaUrl}, but the Context backend URL is ${mixedSavedConnection.backendUrl}.`));
|
|
247
|
+
console.log(chalk.yellow(" That usually means the device was partly pointed at local Umbrella and partly at the live server. Setup can repair that now."));
|
|
248
|
+
}
|
|
249
|
+
let serverUrlInput = options.serverUrl?.trim() || "";
|
|
250
|
+
if (!serverUrlInput) {
|
|
251
|
+
const normalizedSuggestedServer = normalizeUmbrellaServerUrl(suggestedServerUrl);
|
|
252
|
+
const defaultServerChoice = mixedSavedConnection
|
|
253
|
+
? mixedSavedConnection.backendIsLoopback
|
|
254
|
+
? "local"
|
|
255
|
+
: normalizedSuggestedServer === DEFAULT_UMBRELLA_SERVER_URL
|
|
256
|
+
? "live"
|
|
257
|
+
: "custom"
|
|
258
|
+
: normalizedSuggestedServer === DEFAULT_UMBRELLA_SERVER_URL
|
|
259
|
+
? "live"
|
|
260
|
+
: isLocalOnlyServerUrl(normalizedSuggestedServer)
|
|
261
|
+
? "local"
|
|
262
|
+
: "custom";
|
|
263
|
+
const serverChoicePrompt = await prompts({
|
|
264
|
+
choices: [
|
|
265
|
+
{
|
|
266
|
+
description: "Recommended for most people. Uses the shared live Umbrella server.",
|
|
267
|
+
title: `Live Umbrella server (${DEFAULT_UMBRELLA_SERVER_URL})`,
|
|
268
|
+
value: "live",
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
description: "Only use this if Umbrella is running on the same computer right now.",
|
|
272
|
+
title: `Local development server (${LOCAL_UMBRELLA_SERVER_URL})`,
|
|
273
|
+
value: "local",
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
description: mixedSavedConnection
|
|
277
|
+
? "Use this if the saved app/backend URLs were mixed and you want to repair them to some other server."
|
|
278
|
+
: "Type a different server URL yourself.",
|
|
279
|
+
title: "Custom URL",
|
|
280
|
+
value: "custom",
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
initial: ["live", "local", "custom"].indexOf(defaultServerChoice),
|
|
284
|
+
message: "Which Umbrella server should this device use?",
|
|
285
|
+
name: "value",
|
|
286
|
+
type: "select",
|
|
287
|
+
});
|
|
288
|
+
if (!serverChoicePrompt.value) {
|
|
289
|
+
console.log(chalk.red("\n Setup cancelled."));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (serverChoicePrompt.value === "live") {
|
|
293
|
+
serverUrlInput = DEFAULT_UMBRELLA_SERVER_URL;
|
|
294
|
+
}
|
|
295
|
+
else if (serverChoicePrompt.value === "local") {
|
|
296
|
+
serverUrlInput = LOCAL_UMBRELLA_SERVER_URL;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
const customServerPrompt = await prompts({
|
|
300
|
+
initial: defaultServerChoice === "custom"
|
|
301
|
+
? normalizedSuggestedServer
|
|
302
|
+
: DEFAULT_UMBRELLA_SERVER_URL,
|
|
303
|
+
message: "Custom Umbrella server URL",
|
|
304
|
+
name: "value",
|
|
305
|
+
type: "text",
|
|
306
|
+
validate: (value) => {
|
|
307
|
+
try {
|
|
308
|
+
normalizeUmbrellaServerUrl(value);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return "Enter a valid URL like http://5.161.55.138:3100";
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
if (!customServerPrompt.value) {
|
|
317
|
+
console.log(chalk.red("\n Setup cancelled."));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
serverUrlInput = customServerPrompt.value;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const serverUrl = normalizeUmbrellaServerUrl(serverUrlInput ||
|
|
159
324
|
envServerUrl ||
|
|
160
325
|
existing?.umbrellaUrl ||
|
|
161
326
|
existing?.serverUrl ||
|
|
@@ -163,29 +328,24 @@ export function setupCommand(cli) {
|
|
|
163
328
|
try {
|
|
164
329
|
let cookie = null;
|
|
165
330
|
let companies = [];
|
|
331
|
+
let signedInEmail = options.email?.trim() || null;
|
|
332
|
+
console.log(chalk.gray(` Checking ${serverUrl} ...`));
|
|
166
333
|
setUmbrellaAuthChecking(serverUrl);
|
|
167
334
|
const serverCheck = await checkUmbrellaOnboardingServer(serverUrl);
|
|
168
335
|
if (serverCheck.deploymentMode === "authenticated") {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
name: "value",
|
|
172
|
-
type: options.email ? null : "text",
|
|
173
|
-
});
|
|
174
|
-
const passwordPrompt = await prompts({
|
|
175
|
-
message: "Umbrella password",
|
|
176
|
-
name: "value",
|
|
177
|
-
type: options.password ? null : "password",
|
|
178
|
-
});
|
|
179
|
-
const email = options.email?.trim() || emailPrompt.value;
|
|
180
|
-
const password = options.password?.trim() || passwordPrompt.value;
|
|
181
|
-
if (!email || !password) {
|
|
336
|
+
const signInResult = await attemptAuthenticatedSignIn(serverUrl, options);
|
|
337
|
+
if (signInResult.kind === "cancelled") {
|
|
182
338
|
console.log(chalk.red("\n Sign-in cancelled."));
|
|
183
339
|
return;
|
|
184
340
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
341
|
+
if (signInResult.kind === "failure") {
|
|
342
|
+
throw signInResult.error;
|
|
343
|
+
}
|
|
344
|
+
const successfulSignIn = signInResult;
|
|
345
|
+
cookie = successfulSignIn.cookie;
|
|
346
|
+
companies = successfulSignIn.companies;
|
|
347
|
+
signedInEmail = successfulSignIn.email;
|
|
348
|
+
const authSnapshot = setUmbrellaAuthAuthorized(serverUrl, signedInEmail, companies.length);
|
|
189
349
|
console.log(chalk.gray(` Auth state: ${formatUmbrellaAuthStatus(authSnapshot.status)}`));
|
|
190
350
|
if (companies.length === 0) {
|
|
191
351
|
console.log(chalk.yellow("\n Signed in, but this account does not currently see any companies. That usually means the account exists but has not been given board access or company membership yet."));
|
package/dist/commands/space.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import prompts from "prompts";
|
|
3
3
|
import { configManager } from "../config.js";
|
|
4
|
-
import { ensureRepoContext, recordSessionEvent, setSessionPanel } from "../repo-state.js";
|
|
4
|
+
import { ensureRepoContext, findRepoRoot, recordSessionEvent, setSessionPanel } from "../repo-state.js";
|
|
5
5
|
import { pullCommandAction } from "./pull.js";
|
|
6
6
|
import { createContextSpace, getCliSetup, getCompanyContextSummary, toContextSpaces } from "../umbrella.js";
|
|
7
7
|
export async function spaceCommandAction(action, spaceArg) {
|
|
@@ -15,6 +15,7 @@ export async function spaceCommandAction(action, spaceArg) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
const normalizedAction = action.toLowerCase();
|
|
18
|
+
const repoRoot = await findRepoRoot(process.cwd());
|
|
18
19
|
const summary = await getCompanyContextSummary(config.umbrellaUrl, config.companyId);
|
|
19
20
|
const spaces = toContextSpaces(summary);
|
|
20
21
|
if (normalizedAction === "list") {
|
|
@@ -67,7 +68,7 @@ export async function spaceCommandAction(action, spaceArg) {
|
|
|
67
68
|
projectName: setup.activeSpaceName,
|
|
68
69
|
});
|
|
69
70
|
configManager.upsertLocation({
|
|
70
|
-
repoRoot
|
|
71
|
+
repoRoot,
|
|
71
72
|
companyId: setup.companyId,
|
|
72
73
|
companyName: setup.companyName,
|
|
73
74
|
projectId: setup.activeSpaceId,
|
|
@@ -129,7 +130,7 @@ export async function spaceCommandAction(action, spaceArg) {
|
|
|
129
130
|
projectName: setup.activeSpaceName,
|
|
130
131
|
});
|
|
131
132
|
configManager.upsertLocation({
|
|
132
|
-
repoRoot
|
|
133
|
+
repoRoot,
|
|
133
134
|
companyId: setup.companyId,
|
|
134
135
|
companyName: setup.companyName,
|
|
135
136
|
projectId: setup.activeSpaceId,
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type UmbrellaAuthSnapshot } from "../auth-state.js";
|
|
2
|
+
import { type AgentMemoryConfig } from "../config.js";
|
|
2
3
|
export type StatusSnapshot = {
|
|
3
4
|
authSnapshot: UmbrellaAuthSnapshot;
|
|
4
5
|
companyName: string;
|
|
5
6
|
spaceName: string;
|
|
6
7
|
umbrellaUrl: string | null;
|
|
7
8
|
serverUrl: string;
|
|
9
|
+
connectionMode: "saved_local_config" | "verified_live_connection";
|
|
10
|
+
connectionSummary: string;
|
|
8
11
|
currentDirectory: string;
|
|
9
12
|
repoRoot: string;
|
|
10
13
|
umDir: string;
|
|
@@ -26,8 +29,20 @@ export type StatusSnapshot = {
|
|
|
26
29
|
transportQueue: number;
|
|
27
30
|
transportActiveTask: string;
|
|
28
31
|
contextTreeSummary: string;
|
|
32
|
+
warnings: string[];
|
|
29
33
|
nextSteps: string[];
|
|
30
34
|
};
|
|
35
|
+
type OutputFormat = "text" | "json";
|
|
36
|
+
export type ConnectionPresentation = {
|
|
37
|
+
authSnapshot: UmbrellaAuthSnapshot;
|
|
38
|
+
connectionMode: "saved_local_config" | "verified_live_connection";
|
|
39
|
+
connectionSummary: string;
|
|
40
|
+
warnings: string[];
|
|
41
|
+
};
|
|
42
|
+
export declare function buildConnectionPresentation(config: Pick<AgentMemoryConfig, "serverUrl" | "umbrellaUrl">, authSnapshot?: UmbrellaAuthSnapshot): ConnectionPresentation;
|
|
31
43
|
export declare function getStatusSnapshot(): Promise<StatusSnapshot>;
|
|
32
|
-
export declare function statusCommandAction(
|
|
44
|
+
export declare function statusCommandAction(opts?: {
|
|
45
|
+
format?: OutputFormat;
|
|
46
|
+
}): Promise<void>;
|
|
33
47
|
export declare function statusCommand(cli: any): void;
|
|
48
|
+
export {};
|
package/dist/commands/status.js
CHANGED
|
@@ -5,6 +5,46 @@ import { formatUmbrellaAuthStatus } from "../auth-state.js";
|
|
|
5
5
|
import { getStoredUmbrellaAuthSnapshot } from "../adapters/umbrella-auth-runtime.js";
|
|
6
6
|
import { configManager } from "../config.js";
|
|
7
7
|
import { getConnectorRuns, getContextTreeState, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, ensureSessionState, getTransportState, } from "../repo-state.js";
|
|
8
|
+
export function buildConnectionPresentation(config, authSnapshot = getStoredUmbrellaAuthSnapshot()) {
|
|
9
|
+
const warnings = [];
|
|
10
|
+
const connectionMode = authSnapshot.status === "authorized"
|
|
11
|
+
? "verified_live_connection"
|
|
12
|
+
: "saved_local_config";
|
|
13
|
+
const normalizedAuthSnapshot = connectionMode === "saved_local_config" && authSnapshot.status === "checking"
|
|
14
|
+
? {
|
|
15
|
+
...authSnapshot,
|
|
16
|
+
hint: "The last live sign-in check did not finish, so this device is still falling back to saved local setup.",
|
|
17
|
+
message: "Saved local setup is present, but the live Umbrella connection has not been verified yet.",
|
|
18
|
+
status: "not_initialized",
|
|
19
|
+
}
|
|
20
|
+
: authSnapshot;
|
|
21
|
+
const connectionSummary = connectionMode === "verified_live_connection"
|
|
22
|
+
? "This device last completed a real Umbrella sign-in check, so the saved company and space should match a live server session."
|
|
23
|
+
: "This device still has saved local setup details, but the current Umbrella sign-in has not been verified yet. Treat the company and space below as saved local selections until setup succeeds again.";
|
|
24
|
+
if (config.umbrellaUrl && config.serverUrl) {
|
|
25
|
+
try {
|
|
26
|
+
const umbrella = new URL(config.umbrellaUrl);
|
|
27
|
+
const backend = new URL(config.serverUrl);
|
|
28
|
+
const umbrellaIsLoopback = ["127.0.0.1", "localhost"].includes(umbrella.hostname.toLowerCase());
|
|
29
|
+
const backendIsLoopback = ["127.0.0.1", "localhost"].includes(backend.hostname.toLowerCase());
|
|
30
|
+
if (umbrella.hostname !== backend.hostname || umbrella.port !== backend.port) {
|
|
31
|
+
warnings.push(`The saved Umbrella app URL (${config.umbrellaUrl}) and Context backend URL (${config.serverUrl}) point to different places.`);
|
|
32
|
+
}
|
|
33
|
+
if (umbrellaIsLoopback !== backendIsLoopback) {
|
|
34
|
+
warnings.push("One saved URL points to a local-only address while the other points somewhere else. This usually means setup mixed a local app URL with a remote backend URL.");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
warnings.push("One of the saved server URLs could not be parsed cleanly. Re-run setup to refresh the connection details.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
authSnapshot: normalizedAuthSnapshot,
|
|
43
|
+
connectionMode,
|
|
44
|
+
connectionSummary,
|
|
45
|
+
warnings,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
8
48
|
export async function getStatusSnapshot() {
|
|
9
49
|
const config = configManager.config;
|
|
10
50
|
if (!config) {
|
|
@@ -34,6 +74,7 @@ export async function getStatusSnapshot() {
|
|
|
34
74
|
mcpConfigured = false;
|
|
35
75
|
}
|
|
36
76
|
const nextSteps = [];
|
|
77
|
+
const warnings = [];
|
|
37
78
|
if (pending.length > 0)
|
|
38
79
|
nextSteps.push(`Run "umbrella-context push" to share ${pending.length} local draft${pending.length === 1 ? "" : "s"}.`);
|
|
39
80
|
if (!activeProvider || !config.activeModel) {
|
|
@@ -45,12 +86,18 @@ export async function getStatusSnapshot() {
|
|
|
45
86
|
nextSteps.push('Add a first repo connector with "umbrella-context connectors install".');
|
|
46
87
|
if (hubEntries.length === 0)
|
|
47
88
|
nextSteps.push('Browse reusable bundles with "umbrella-context hub list".');
|
|
89
|
+
const connection = buildConnectionPresentation(config);
|
|
90
|
+
if (connection.connectionMode === "saved_local_config") {
|
|
91
|
+
nextSteps.unshift('Re-run "umbrella-context setup" to verify the live Umbrella connection before trusting the saved company and space.');
|
|
92
|
+
}
|
|
48
93
|
return {
|
|
49
|
-
authSnapshot:
|
|
94
|
+
authSnapshot: connection.authSnapshot,
|
|
50
95
|
companyName: config.companyName,
|
|
51
96
|
spaceName: config.projectName,
|
|
52
97
|
umbrellaUrl: config.umbrellaUrl ?? null,
|
|
53
98
|
serverUrl: config.serverUrl,
|
|
99
|
+
connectionMode: connection.connectionMode,
|
|
100
|
+
connectionSummary: connection.connectionSummary,
|
|
54
101
|
currentDirectory: process.cwd(),
|
|
55
102
|
repoRoot,
|
|
56
103
|
umDir,
|
|
@@ -74,72 +121,89 @@ export async function getStatusSnapshot() {
|
|
|
74
121
|
transportQueue: transport?.queue.length ?? 0,
|
|
75
122
|
transportActiveTask: transport?.activeTaskId ?? "None",
|
|
76
123
|
contextTreeSummary: contextTree?.summaryHandle ?? "No context tree summary yet",
|
|
124
|
+
warnings: connection.warnings,
|
|
77
125
|
nextSteps,
|
|
78
126
|
};
|
|
79
127
|
}
|
|
80
|
-
|
|
128
|
+
function printStatusJson(snapshot) {
|
|
129
|
+
console.log(JSON.stringify({ ok: true, action: "status", ...snapshot }, null, 2));
|
|
130
|
+
}
|
|
131
|
+
function formatTextConnectionMode(mode) {
|
|
132
|
+
return mode === "verified_live_connection" ? "Verified live connection" : "Saved local config only";
|
|
133
|
+
}
|
|
134
|
+
function printBullets(items) {
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
console.log(`- ${item}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export async function statusCommandAction(opts = {}) {
|
|
81
140
|
let snapshot;
|
|
82
141
|
try {
|
|
83
142
|
snapshot = await getStatusSnapshot();
|
|
84
143
|
}
|
|
85
144
|
catch (err) {
|
|
145
|
+
if (opts.format === "json") {
|
|
146
|
+
console.log(JSON.stringify({ ok: false, action: "status", error: err.message }, null, 2));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
86
149
|
console.log(chalk.red(err.message));
|
|
87
150
|
return;
|
|
88
151
|
}
|
|
152
|
+
if (opts.format === "json") {
|
|
153
|
+
printStatusJson(snapshot);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
89
156
|
console.log(chalk.bold("\n Umbrella Context Status\n"));
|
|
90
|
-
console.log(
|
|
91
|
-
console.log(`
|
|
92
|
-
console.log(`
|
|
93
|
-
console.log(`
|
|
94
|
-
console.log(`
|
|
95
|
-
console.log(`
|
|
96
|
-
console.log(`
|
|
97
|
-
console.log(` Auth Hint: ${snapshot.authSnapshot.hint ?? "No auth hint recorded yet"}`);
|
|
98
|
-
console.log("");
|
|
99
|
-
console.log(chalk.cyan(" Connection"));
|
|
100
|
-
console.log(` Company: ${snapshot.companyName}`);
|
|
101
|
-
console.log(` Space: ${snapshot.spaceName}`);
|
|
102
|
-
console.log(` Umbrella: ${snapshot.umbrellaUrl ?? "Not saved"}`);
|
|
103
|
-
console.log(` Context Backend: ${snapshot.serverUrl}`);
|
|
157
|
+
console.log(`Connection: ${formatTextConnectionMode(snapshot.connectionMode)}`);
|
|
158
|
+
console.log(`Auth: ${formatUmbrellaAuthStatus(snapshot.authSnapshot.status)}`);
|
|
159
|
+
console.log(`Workspace: ${snapshot.companyName} / ${snapshot.spaceName}`);
|
|
160
|
+
console.log(`Current Directory: ${snapshot.currentDirectory}`);
|
|
161
|
+
console.log(`Provider: ${snapshot.providerLabel}`);
|
|
162
|
+
console.log(`Model: ${snapshot.modelLabel}`);
|
|
163
|
+
console.log(`Context Tree: ${snapshot.contextTreeSummary}`);
|
|
104
164
|
console.log("");
|
|
105
|
-
console.log(chalk.cyan("
|
|
106
|
-
console.log(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
console.log(` Pulled Context Snapshot: ${snapshot.pulledContextCount}`);
|
|
111
|
-
console.log(` Pulled Known Fixes: ${snapshot.pulledFixCount}`);
|
|
112
|
-
console.log(` Last Push: ${snapshot.lastPushAt}`);
|
|
113
|
-
console.log(` Last Pull: ${snapshot.lastPullAt}`);
|
|
114
|
-
console.log("");
|
|
115
|
-
console.log(chalk.cyan(" Repo Ecosystem"));
|
|
116
|
-
console.log(` Hub Entries Installed: ${snapshot.hubEntriesCount}`);
|
|
117
|
-
console.log(` Connectors Installed: ${snapshot.connectorsCount}`);
|
|
118
|
-
console.log(` Repo MCP Wired: ${snapshot.mcpConfigured ? "Yes" : "No"}`);
|
|
119
|
-
console.log(` Last Connector Run: ${snapshot.latestConnectorRun}`);
|
|
165
|
+
console.log(chalk.cyan("Summary"));
|
|
166
|
+
console.log(snapshot.connectionSummary);
|
|
167
|
+
if (snapshot.authSnapshot.message) {
|
|
168
|
+
console.log(snapshot.authSnapshot.message);
|
|
169
|
+
}
|
|
120
170
|
console.log("");
|
|
121
|
-
console.log(chalk.cyan("
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.log(` Active Task: ${snapshot.transportActiveTask}`);
|
|
171
|
+
console.log(chalk.cyan("Local Context"));
|
|
172
|
+
printBullets([
|
|
173
|
+
`${snapshot.pendingCount} pending draft${snapshot.pendingCount === 1 ? "" : "s"}`,
|
|
174
|
+
`${snapshot.pulledContextCount} pulled note${snapshot.pulledContextCount === 1 ? "" : "s"}`,
|
|
175
|
+
`${snapshot.pulledFixCount} pulled known fix${snapshot.pulledFixCount === 1 ? "" : "es"}`,
|
|
176
|
+
`Last push: ${snapshot.lastPushAt}`,
|
|
177
|
+
`Last pull: ${snapshot.lastPullAt}`,
|
|
178
|
+
]);
|
|
130
179
|
console.log("");
|
|
131
|
-
console.log(chalk.cyan("
|
|
132
|
-
|
|
180
|
+
console.log(chalk.cyan("Runtime"));
|
|
181
|
+
printBullets([
|
|
182
|
+
`Panel: ${snapshot.currentPanel}`,
|
|
183
|
+
`Focus: ${snapshot.currentFocus}`,
|
|
184
|
+
`Latest event: ${snapshot.latestEvent}`,
|
|
185
|
+
`Transport: ${snapshot.transportStatus}`,
|
|
186
|
+
`Queue: ${snapshot.transportQueue}`,
|
|
187
|
+
`Active task: ${snapshot.transportActiveTask}`,
|
|
188
|
+
]);
|
|
189
|
+
if (snapshot.warnings.length > 0) {
|
|
190
|
+
console.log("");
|
|
191
|
+
console.log(chalk.yellow(" Warnings"));
|
|
192
|
+
printBullets(snapshot.warnings);
|
|
193
|
+
}
|
|
133
194
|
if (snapshot.nextSteps.length > 0) {
|
|
134
195
|
console.log("");
|
|
135
196
|
console.log(chalk.cyan(" Suggested Next Steps"));
|
|
136
|
-
snapshot.nextSteps
|
|
137
|
-
console.log(` - ${step}`);
|
|
138
|
-
});
|
|
197
|
+
printBullets(snapshot.nextSteps);
|
|
139
198
|
}
|
|
140
199
|
}
|
|
141
200
|
export function statusCommand(cli) {
|
|
142
|
-
cli
|
|
143
|
-
|
|
201
|
+
cli
|
|
202
|
+
.command("status", "Show the current repo, company, space, and local sync state")
|
|
203
|
+
.option("--format <format>", "Output format (text or json)")
|
|
204
|
+
.example("status")
|
|
205
|
+
.example("status --format json")
|
|
206
|
+
.action(async (opts) => {
|
|
207
|
+
await statusCommandAction(opts);
|
|
144
208
|
});
|
|
145
209
|
}
|