umbrella-context 0.1.34 → 0.1.35
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/adapters/umbrella-auth-runtime.d.ts +5 -0
- package/dist/adapters/umbrella-auth-runtime.js +116 -0
- package/dist/auth-state.d.ts +12 -0
- package/dist/auth-state.js +27 -0
- package/dist/commands/setup.js +47 -1
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +12 -0
- package/dist/commands/tui.js +26 -5
- package/dist/config.d.ts +7 -0
- package/dist/config.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/umbrella.d.ts +12 -0
- package/dist/umbrella.js +38 -9
- package/package.json +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type UmbrellaAuthSnapshot } from "../auth-state.js";
|
|
2
|
+
export declare function getStoredUmbrellaAuthSnapshot(): UmbrellaAuthSnapshot;
|
|
3
|
+
export declare function setUmbrellaAuthChecking(serverUrl: string, email?: string | null): UmbrellaAuthSnapshot;
|
|
4
|
+
export declare function setUmbrellaAuthAuthorized(serverUrl: string, email: string | null, companiesVisible: number): UmbrellaAuthSnapshot;
|
|
5
|
+
export declare function setUmbrellaAuthFailure(serverUrl: string, error: unknown, email?: string | null): UmbrellaAuthSnapshot;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { configManager } from "../config.js";
|
|
2
|
+
import { createDefaultUmbrellaAuthSnapshot, } from "../auth-state.js";
|
|
3
|
+
import { UmbrellaRequestError } from "../umbrella.js";
|
|
4
|
+
function nowIso() {
|
|
5
|
+
return new Date().toISOString();
|
|
6
|
+
}
|
|
7
|
+
function pathnameFor(url) {
|
|
8
|
+
try {
|
|
9
|
+
return new URL(url).pathname;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return url;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function getStoredUmbrellaAuthSnapshot() {
|
|
16
|
+
return configManager.authSnapshot ?? createDefaultUmbrellaAuthSnapshot();
|
|
17
|
+
}
|
|
18
|
+
export function setUmbrellaAuthChecking(serverUrl, email) {
|
|
19
|
+
const snapshot = {
|
|
20
|
+
checkedAt: nowIso(),
|
|
21
|
+
companiesVisible: null,
|
|
22
|
+
email: email?.trim() || null,
|
|
23
|
+
hint: "Checking the live Umbrella sign-in flow.",
|
|
24
|
+
message: "Trying to sign in and validate company access.",
|
|
25
|
+
serverUrl,
|
|
26
|
+
status: "checking",
|
|
27
|
+
};
|
|
28
|
+
configManager.setAuthSnapshot(snapshot);
|
|
29
|
+
return snapshot;
|
|
30
|
+
}
|
|
31
|
+
export function setUmbrellaAuthAuthorized(serverUrl, email, companiesVisible) {
|
|
32
|
+
const snapshot = {
|
|
33
|
+
checkedAt: nowIso(),
|
|
34
|
+
companiesVisible,
|
|
35
|
+
email: email?.trim() || null,
|
|
36
|
+
hint: companiesVisible > 0
|
|
37
|
+
? "This account can see companies and should be able to continue setup."
|
|
38
|
+
: "This account signed in, but no companies are visible yet.",
|
|
39
|
+
message: companiesVisible > 0
|
|
40
|
+
? `Signed in successfully and loaded ${companiesVisible} compan${companiesVisible === 1 ? "y" : "ies"}.`
|
|
41
|
+
: "Signed in successfully, but this account does not currently see any companies.",
|
|
42
|
+
serverUrl,
|
|
43
|
+
status: "authorized",
|
|
44
|
+
};
|
|
45
|
+
configManager.setAuthSnapshot(snapshot);
|
|
46
|
+
return snapshot;
|
|
47
|
+
}
|
|
48
|
+
export function setUmbrellaAuthFailure(serverUrl, error, email) {
|
|
49
|
+
let snapshot;
|
|
50
|
+
if (error instanceof UmbrellaRequestError) {
|
|
51
|
+
const path = pathnameFor(error.url);
|
|
52
|
+
if (error.status === 401 && error.code === "INVALID_EMAIL_OR_PASSWORD") {
|
|
53
|
+
snapshot = {
|
|
54
|
+
checkedAt: nowIso(),
|
|
55
|
+
companiesVisible: null,
|
|
56
|
+
email: email?.trim() || null,
|
|
57
|
+
hint: "Double-check the email address and password on that other computer.",
|
|
58
|
+
message: "Umbrella rejected the email or password.",
|
|
59
|
+
serverUrl,
|
|
60
|
+
status: "unauthorized",
|
|
61
|
+
};
|
|
62
|
+
configManager.setAuthSnapshot(snapshot);
|
|
63
|
+
return snapshot;
|
|
64
|
+
}
|
|
65
|
+
if (error.status === 401 && path.endsWith("/api/auth/get-session")) {
|
|
66
|
+
snapshot = {
|
|
67
|
+
checkedAt: nowIso(),
|
|
68
|
+
companiesVisible: null,
|
|
69
|
+
email: email?.trim() || null,
|
|
70
|
+
hint: "The live app accepted the sign-in request, but it did not turn into a usable board session afterward.",
|
|
71
|
+
message: "The session was not established after sign-in.",
|
|
72
|
+
serverUrl,
|
|
73
|
+
status: "session_error",
|
|
74
|
+
};
|
|
75
|
+
configManager.setAuthSnapshot(snapshot);
|
|
76
|
+
return snapshot;
|
|
77
|
+
}
|
|
78
|
+
if (error.status === 403 && path.endsWith("/api/companies")) {
|
|
79
|
+
snapshot = {
|
|
80
|
+
checkedAt: nowIso(),
|
|
81
|
+
companiesVisible: null,
|
|
82
|
+
email: email?.trim() || null,
|
|
83
|
+
hint: "This usually means the account exists, but it still needs board ownership or company membership on the live Umbrella server.",
|
|
84
|
+
message: "Signed in reached Umbrella, but company access was blocked.",
|
|
85
|
+
serverUrl,
|
|
86
|
+
status: "forbidden",
|
|
87
|
+
};
|
|
88
|
+
configManager.setAuthSnapshot(snapshot);
|
|
89
|
+
return snapshot;
|
|
90
|
+
}
|
|
91
|
+
if (error.status === 403 && path.includes("/learning")) {
|
|
92
|
+
snapshot = {
|
|
93
|
+
checkedAt: nowIso(),
|
|
94
|
+
companiesVisible: null,
|
|
95
|
+
email: email?.trim() || null,
|
|
96
|
+
hint: "The account can reach Umbrella, but it is blocked from this company's Context area.",
|
|
97
|
+
message: "Company Context access was blocked.",
|
|
98
|
+
serverUrl,
|
|
99
|
+
status: "forbidden",
|
|
100
|
+
};
|
|
101
|
+
configManager.setAuthSnapshot(snapshot);
|
|
102
|
+
return snapshot;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
snapshot = {
|
|
106
|
+
checkedAt: nowIso(),
|
|
107
|
+
companiesVisible: null,
|
|
108
|
+
email: email?.trim() || null,
|
|
109
|
+
hint: "Check the server URL and the live Umbrella deployment state.",
|
|
110
|
+
message: error instanceof Error ? error.message : "Unexpected auth failure",
|
|
111
|
+
serverUrl,
|
|
112
|
+
status: "session_error",
|
|
113
|
+
};
|
|
114
|
+
configManager.setAuthSnapshot(snapshot);
|
|
115
|
+
return snapshot;
|
|
116
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type UmbrellaAuthStatus = "not_initialized" | "checking" | "unauthorized" | "authorized" | "forbidden" | "session_error";
|
|
2
|
+
export interface UmbrellaAuthSnapshot {
|
|
3
|
+
checkedAt: string;
|
|
4
|
+
companiesVisible: number | null;
|
|
5
|
+
email: string | null;
|
|
6
|
+
hint: string | null;
|
|
7
|
+
message: string | null;
|
|
8
|
+
serverUrl: string | null;
|
|
9
|
+
status: UmbrellaAuthStatus;
|
|
10
|
+
}
|
|
11
|
+
export declare function createDefaultUmbrellaAuthSnapshot(): UmbrellaAuthSnapshot;
|
|
12
|
+
export declare function formatUmbrellaAuthStatus(status: UmbrellaAuthStatus): "Checking" | "Unauthorized" | "Authorized" | "Access Blocked" | "Session Problem" | "Not initialized";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function createDefaultUmbrellaAuthSnapshot() {
|
|
2
|
+
return {
|
|
3
|
+
checkedAt: new Date(0).toISOString(),
|
|
4
|
+
companiesVisible: null,
|
|
5
|
+
email: null,
|
|
6
|
+
hint: null,
|
|
7
|
+
message: null,
|
|
8
|
+
serverUrl: null,
|
|
9
|
+
status: "not_initialized",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function formatUmbrellaAuthStatus(status) {
|
|
13
|
+
switch (status) {
|
|
14
|
+
case "checking":
|
|
15
|
+
return "Checking";
|
|
16
|
+
case "unauthorized":
|
|
17
|
+
return "Unauthorized";
|
|
18
|
+
case "authorized":
|
|
19
|
+
return "Authorized";
|
|
20
|
+
case "forbidden":
|
|
21
|
+
return "Access Blocked";
|
|
22
|
+
case "session_error":
|
|
23
|
+
return "Session Problem";
|
|
24
|
+
default:
|
|
25
|
+
return "Not initialized";
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import prompts from "prompts";
|
|
3
|
+
import { formatUmbrellaAuthStatus } from "../auth-state.js";
|
|
3
4
|
import { configManager } from "../config.js";
|
|
5
|
+
import { UmbrellaRequestError } from "../umbrella.js";
|
|
6
|
+
import { setUmbrellaAuthAuthorized, setUmbrellaAuthChecking, setUmbrellaAuthFailure, } from "../adapters/umbrella-auth-runtime.js";
|
|
4
7
|
import { checkUmbrellaOnboardingServer, connectUmbrellaDevice, createUmbrellaOnboardingCompany, createUmbrellaOnboardingSpace, loadUmbrellaOnboardingSpaces, normalizeUmbrellaServerUrl, signInUmbrellaOnboarding, } from "../adapters/umbrella-onboarding.js";
|
|
5
8
|
const DEFAULT_UMBRELLA_SERVER_URL = "http://5.161.55.138:3100";
|
|
6
9
|
function isLocalOnlyServerUrl(value) {
|
|
@@ -22,6 +25,35 @@ function printConnected(setup) {
|
|
|
22
25
|
console.log(chalk.gray(" umbrella-context curate \"We learned that...\""));
|
|
23
26
|
console.log(chalk.gray(" umbrella-context mcp"));
|
|
24
27
|
}
|
|
28
|
+
function describeSetupFailure(error) {
|
|
29
|
+
if (!(error instanceof UmbrellaRequestError)) {
|
|
30
|
+
return error instanceof Error ? error.message : "Unexpected setup failure";
|
|
31
|
+
}
|
|
32
|
+
const path = (() => {
|
|
33
|
+
try {
|
|
34
|
+
return new URL(error.url).pathname;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return error.url;
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
if (error.status === 401 && error.code === "INVALID_EMAIL_OR_PASSWORD") {
|
|
41
|
+
return "Umbrella rejected that email or password. Double-check both and try again.";
|
|
42
|
+
}
|
|
43
|
+
if (error.status === 401 && path.endsWith("/api/auth/get-session")) {
|
|
44
|
+
return "Umbrella accepted the sign-in request, but no board session was established afterward. This usually means the live auth cookie is not being created the way the CLI expects.";
|
|
45
|
+
}
|
|
46
|
+
if (error.status === 403 && path.endsWith("/api/companies")) {
|
|
47
|
+
return "This account reached Umbrella, but it is not allowed to load companies yet. Usually that means the account is signed in but does not have board ownership or company access on the live server.";
|
|
48
|
+
}
|
|
49
|
+
if (error.status === 403 && path.includes("/learning")) {
|
|
50
|
+
return "This account can reach Umbrella, but it is blocked from that company's Context area. Usually that means the account is not attached to that company yet.";
|
|
51
|
+
}
|
|
52
|
+
if (error.status === 403) {
|
|
53
|
+
return `Umbrella refused this request with 403 Forbidden while calling ${path}. Usually that means this account is authenticated but does not have permission for that step yet.`;
|
|
54
|
+
}
|
|
55
|
+
return error.message;
|
|
56
|
+
}
|
|
25
57
|
async function chooseCompany(serverUrl, companies, cookie, initialCompanyId) {
|
|
26
58
|
if (initialCompanyId) {
|
|
27
59
|
const matched = companies.find((company) => company.id === initialCompanyId);
|
|
@@ -122,6 +154,7 @@ export function setupCommand(cli) {
|
|
|
122
154
|
try {
|
|
123
155
|
let cookie = null;
|
|
124
156
|
let companies = [];
|
|
157
|
+
setUmbrellaAuthChecking(serverUrl);
|
|
125
158
|
const serverCheck = await checkUmbrellaOnboardingServer(serverUrl);
|
|
126
159
|
if (serverCheck.deploymentMode === "authenticated") {
|
|
127
160
|
const emailPrompt = await prompts({
|
|
@@ -143,8 +176,16 @@ export function setupCommand(cli) {
|
|
|
143
176
|
const signIn = await signInUmbrellaOnboarding(serverUrl, email, password);
|
|
144
177
|
cookie = signIn.cookie;
|
|
145
178
|
companies = signIn.companies;
|
|
179
|
+
const authSnapshot = setUmbrellaAuthAuthorized(serverUrl, email, companies.length);
|
|
180
|
+
console.log(chalk.gray(` Auth state: ${formatUmbrellaAuthStatus(authSnapshot.status)}`));
|
|
181
|
+
if (companies.length === 0) {
|
|
182
|
+
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."));
|
|
183
|
+
console.log(chalk.yellow(" If this should be your main Umbrella account, it may still need the board-claim/admin step on the live server."));
|
|
184
|
+
}
|
|
146
185
|
}
|
|
147
186
|
else {
|
|
187
|
+
const authSnapshot = setUmbrellaAuthAuthorized(serverUrl, null, serverCheck.companies.length);
|
|
188
|
+
console.log(chalk.gray(` Auth state: ${formatUmbrellaAuthStatus(authSnapshot.status)}`));
|
|
148
189
|
console.log(chalk.gray(" Local trusted mode detected. Using the local board access path."));
|
|
149
190
|
companies = serverCheck.companies;
|
|
150
191
|
}
|
|
@@ -167,7 +208,12 @@ export function setupCommand(cli) {
|
|
|
167
208
|
console.log(chalk.yellow("\n Hint: 127.0.0.1 points to this same computer only."));
|
|
168
209
|
console.log(chalk.yellow(" If Umbrella is running on another machine or server, re-run setup and enter that machine's real URL or IP."));
|
|
169
210
|
}
|
|
170
|
-
|
|
211
|
+
const authSnapshot = setUmbrellaAuthFailure(serverUrl, err, options.email?.trim() || null);
|
|
212
|
+
console.log(chalk.red(` Auth state: ${formatUmbrellaAuthStatus(authSnapshot.status)}`));
|
|
213
|
+
if (authSnapshot.hint) {
|
|
214
|
+
console.log(chalk.yellow(` Hint: ${authSnapshot.hint}`));
|
|
215
|
+
}
|
|
216
|
+
console.log(chalk.red(`\n Error: ${describeSetupFailure(err)}`));
|
|
171
217
|
}
|
|
172
218
|
});
|
|
173
219
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { formatUmbrellaAuthStatus } from "../auth-state.js";
|
|
5
|
+
import { getStoredUmbrellaAuthSnapshot } from "../adapters/umbrella-auth-runtime.js";
|
|
4
6
|
import { configManager } from "../config.js";
|
|
5
7
|
import { getConnectorRuns, getContextTreeState, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, ensureSessionState, getTransportState, } from "../repo-state.js";
|
|
6
8
|
export async function getStatusSnapshot() {
|
|
@@ -44,6 +46,7 @@ export async function getStatusSnapshot() {
|
|
|
44
46
|
if (hubEntries.length === 0)
|
|
45
47
|
nextSteps.push('Browse reusable bundles with "umbrella-context hub list".');
|
|
46
48
|
return {
|
|
49
|
+
authSnapshot: getStoredUmbrellaAuthSnapshot(),
|
|
47
50
|
companyName: config.companyName,
|
|
48
51
|
spaceName: config.projectName,
|
|
49
52
|
umbrellaUrl: config.umbrellaUrl ?? null,
|
|
@@ -84,6 +87,15 @@ export async function statusCommandAction() {
|
|
|
84
87
|
return;
|
|
85
88
|
}
|
|
86
89
|
console.log(chalk.bold("\n Umbrella Context Status\n"));
|
|
90
|
+
console.log(chalk.cyan(" Auth"));
|
|
91
|
+
console.log(` State: ${formatUmbrellaAuthStatus(snapshot.authSnapshot.status)}`);
|
|
92
|
+
console.log(` Checked At: ${snapshot.authSnapshot.checkedAt === new Date(0).toISOString() ? "Never" : snapshot.authSnapshot.checkedAt}`);
|
|
93
|
+
console.log(` Auth Server: ${snapshot.authSnapshot.serverUrl ?? "Not checked yet"}`);
|
|
94
|
+
console.log(` Email: ${snapshot.authSnapshot.email ?? "Not saved"}`);
|
|
95
|
+
console.log(` Companies Visible: ${snapshot.authSnapshot.companiesVisible === null ? "Unknown" : String(snapshot.authSnapshot.companiesVisible)}`);
|
|
96
|
+
console.log(` Auth Message: ${snapshot.authSnapshot.message ?? "No auth check has been saved yet"}`);
|
|
97
|
+
console.log(` Auth Hint: ${snapshot.authSnapshot.hint ?? "No auth hint recorded yet"}`);
|
|
98
|
+
console.log("");
|
|
87
99
|
console.log(chalk.cyan(" Connection"));
|
|
88
100
|
console.log(` Company: ${snapshot.companyName}`);
|
|
89
101
|
console.log(` Space: ${snapshot.spaceName}`);
|
package/dist/commands/tui.js
CHANGED
|
@@ -4,11 +4,13 @@ import { Box, render, Text, useApp, useInput } from "ink";
|
|
|
4
4
|
import TextInput from "ink-text-input";
|
|
5
5
|
import Spinner from "ink-spinner";
|
|
6
6
|
import chalk from "chalk";
|
|
7
|
+
import { formatUmbrellaAuthStatus } from "../auth-state.js";
|
|
7
8
|
import { getUmbrellaContextRuntimeSummary, useUmbrellaContextRuntimeBridgeStore, hydrateUmbrellaContextRuntimeBridgeFromSnapshot, } from "../adapters/byterover-context-runtime-store.js";
|
|
8
9
|
import { getUmbrellaTransportTaskBridgeSummary, useUmbrellaTransportTaskBridgeStore, hydrateUmbrellaTransportTaskBridgeFromSnapshot, } from "../adapters/byterover-transport-task-store.js";
|
|
9
10
|
import { buildVendorRuntimeBridgeSnapshot, } from "../adapters/byterover-runtime-bridge.js";
|
|
10
11
|
import { addPendingMemory, completeTask, createTask, ensureRepoContext, getContextTreeState, ensureSessionState, getConnectorRuns, getInstalledConnectors, getInstalledHubEntries, getPendingMemories, getPulledFixes, getPulledMemories, getRepoContext, getSessionState, getTasks, getTransportState, recordSessionEvent, setSessionPanel, summarizeLocalMemoryMatches, } from "../repo-state.js";
|
|
11
12
|
import { configManager } from "../config.js";
|
|
13
|
+
import { getStoredUmbrellaAuthSnapshot, setUmbrellaAuthAuthorized, setUmbrellaAuthChecking, setUmbrellaAuthFailure, } from "../adapters/umbrella-auth-runtime.js";
|
|
12
14
|
import { checkUmbrellaOnboardingServer, connectUmbrellaDevice, createUmbrellaOnboardingCompany, createUmbrellaOnboardingSpace, loadUmbrellaOnboardingSpaces, signInUmbrellaOnboarding, } from "../adapters/umbrella-onboarding.js";
|
|
13
15
|
import { buildProviderDraftValue, connectSavedProviderFromDraft, createEmptyProviderConnectDraft, UMBRELLA_PROVIDER_CHOICES, updateProviderDraftValue, } from "../adapters/umbrella-provider-runtime.js";
|
|
14
16
|
import { connectorsCommandAction, listConnectorTemplates } from "./connectors.js";
|
|
@@ -200,8 +202,8 @@ function PanelShell({ activePanel, children, footer, message, selectedPanelIndex
|
|
|
200
202
|
}) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", paddingX: 1, paddingY: 1, children: children })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [message ? _jsx(Text, { color: "green", children: message }) : null, _jsx(Text, { color: "gray", children: footer })] })] }));
|
|
201
203
|
}
|
|
202
204
|
function SetupView(props) {
|
|
203
|
-
const { busyLabel, companies, config, inputValue, message, onInputChange, selectedCompanyIndex, selectedSpaceIndex, serverUrl, spaces, step, } = props;
|
|
204
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: config ? "Connected Device" : "Connect This Device" }), _jsx(Text, { color: "gray", children: "Sign into Umbrella, choose a company, then choose the team space for this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Server", value: serverUrl || "Not set" }), _jsx(StatusLine, { label: "Step", value: step }), config ? _jsx(StatusLine, { label: "Current link", value: `${config.companyName} / ${config.projectName}` }) : null] }), busyLabel ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), " ", busyLabel] }) })) : null, message ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: message }) })) : null, (step === "server" || step === "email" || step === "password" || step === "company-create" || step === "space-create") ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: step === "server" ? "Server URL" :
|
|
205
|
+
const { busyLabel, companies, config, authSnapshot, inputValue, message, onInputChange, selectedCompanyIndex, selectedSpaceIndex, serverUrl, spaces, step, } = props;
|
|
206
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: config ? "Connected Device" : "Connect This Device" }), _jsx(Text, { color: "gray", children: "Sign into Umbrella, choose a company, then choose the team space for this repo." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StatusLine, { label: "Server", value: serverUrl || "Not set" }), _jsx(StatusLine, { label: "Step", value: step }), _jsx(StatusLine, { label: "Auth state", value: formatUmbrellaAuthStatus(authSnapshot.status) }), _jsx(StatusLine, { label: "Auth message", value: authSnapshot.message ?? "No auth check yet" }), _jsx(StatusLine, { label: "Auth hint", value: authSnapshot.hint ?? "No auth hint yet" }), config ? _jsx(StatusLine, { label: "Current link", value: `${config.companyName} / ${config.projectName}` }) : null] }), busyLabel ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), " ", busyLabel] }) })) : null, message ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: message }) })) : null, (step === "server" || step === "email" || step === "password" || step === "company-create" || step === "space-create") ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: step === "server" ? "Server URL" :
|
|
205
207
|
step === "email" ? "Umbrella email" :
|
|
206
208
|
step === "password" ? "Umbrella password" :
|
|
207
209
|
step === "company-create" ? "New company name" :
|
|
@@ -224,6 +226,7 @@ function App() {
|
|
|
224
226
|
const [setupStep, setSetupStep] = useState(configManager.config ? "done" : "server");
|
|
225
227
|
const [serverUrl, setServerUrl] = useState(configManager.config?.umbrellaUrl ?? configManager.config?.serverUrl ?? "http://127.0.0.1:3100");
|
|
226
228
|
const [setupEmail, setSetupEmail] = useState("");
|
|
229
|
+
const [authSnapshot, setAuthSnapshot] = useState(getStoredUmbrellaAuthSnapshot());
|
|
227
230
|
const [setupCookie, setSetupCookie] = useState(null);
|
|
228
231
|
const [companies, setCompanies] = useState([]);
|
|
229
232
|
const [selectedCompanyIndex, setSelectedCompanyIndex] = useState(0);
|
|
@@ -254,6 +257,7 @@ function App() {
|
|
|
254
257
|
if (configManager.config) {
|
|
255
258
|
await ensureRepoContext(configManager.config);
|
|
256
259
|
}
|
|
260
|
+
setAuthSnapshot(getStoredUmbrellaAuthSnapshot());
|
|
257
261
|
const vendorSnapshot = await buildVendorRuntimeBridgeSnapshot();
|
|
258
262
|
hydrateUmbrellaTransportTaskBridgeFromSnapshot(vendorSnapshot);
|
|
259
263
|
hydrateUmbrellaContextRuntimeBridgeFromSnapshot(vendorSnapshot);
|
|
@@ -499,18 +503,24 @@ function App() {
|
|
|
499
503
|
setInputValue("");
|
|
500
504
|
setBusyLabel("Checking Umbrella server...");
|
|
501
505
|
try {
|
|
506
|
+
setAuthSnapshot(setUmbrellaAuthChecking(nextServer, setupEmail || null));
|
|
502
507
|
const serverCheck = await checkUmbrellaOnboardingServer(nextServer);
|
|
503
508
|
if (serverCheck.deploymentMode === "authenticated") {
|
|
504
509
|
setSetupStep("email");
|
|
510
|
+
setMessage("Umbrella is reachable. Enter the email for the account you want to use on this device.");
|
|
505
511
|
}
|
|
506
512
|
else {
|
|
513
|
+
setAuthSnapshot(setUmbrellaAuthAuthorized(nextServer, null, serverCheck.companies.length));
|
|
507
514
|
setCompanies(serverCheck.companies);
|
|
508
515
|
setSelectedCompanyIndex(0);
|
|
509
516
|
setSetupStep("company");
|
|
517
|
+
setMessage("Umbrella is reachable and trusted mode is active. Choose a company next.");
|
|
510
518
|
}
|
|
511
519
|
}
|
|
512
520
|
catch (error) {
|
|
513
|
-
|
|
521
|
+
const snapshot = setUmbrellaAuthFailure(nextServer, error, setupEmail || null);
|
|
522
|
+
setAuthSnapshot(snapshot);
|
|
523
|
+
setMessage(`Could not reach Umbrella: ${snapshot.message}${snapshot.hint ? ` ${snapshot.hint}` : ""}`);
|
|
514
524
|
}
|
|
515
525
|
finally {
|
|
516
526
|
setBusyLabel(null);
|
|
@@ -527,14 +537,21 @@ function App() {
|
|
|
527
537
|
setInputValue("");
|
|
528
538
|
setBusyLabel("Signing into Umbrella...");
|
|
529
539
|
try {
|
|
540
|
+
setAuthSnapshot(setUmbrellaAuthChecking(serverUrl, setupEmail));
|
|
530
541
|
const signIn = await signInUmbrellaOnboarding(serverUrl, setupEmail, nextValue);
|
|
531
542
|
setSetupCookie(signIn.cookie);
|
|
532
543
|
setCompanies(signIn.companies);
|
|
533
544
|
setSelectedCompanyIndex(0);
|
|
545
|
+
setAuthSnapshot(setUmbrellaAuthAuthorized(serverUrl, setupEmail, signIn.companies.length));
|
|
534
546
|
setSetupStep("company");
|
|
547
|
+
setMessage(signIn.companies.length > 0
|
|
548
|
+
? `Signed in successfully. Choose one of the ${signIn.companies.length} compan${signIn.companies.length === 1 ? "y" : "ies"} next.`
|
|
549
|
+
: "Signed in successfully, but this account does not see any companies yet.");
|
|
535
550
|
}
|
|
536
551
|
catch (error) {
|
|
537
|
-
|
|
552
|
+
const snapshot = setUmbrellaAuthFailure(serverUrl, error, setupEmail);
|
|
553
|
+
setAuthSnapshot(snapshot);
|
|
554
|
+
setMessage(`Sign-in failed: ${snapshot.message}${snapshot.hint ? ` ${snapshot.hint}` : ""}`);
|
|
538
555
|
}
|
|
539
556
|
finally {
|
|
540
557
|
setBusyLabel(null);
|
|
@@ -553,6 +570,7 @@ function App() {
|
|
|
553
570
|
setSelectedSpaceIndex(0);
|
|
554
571
|
setInputValue("");
|
|
555
572
|
setSetupStep("space");
|
|
573
|
+
setMessage(`Created ${company.name}. Choose the space this repo should use next.`);
|
|
556
574
|
}
|
|
557
575
|
catch (error) {
|
|
558
576
|
setMessage(`Could not create company: ${error.message}`);
|
|
@@ -571,6 +589,7 @@ function App() {
|
|
|
571
589
|
setSelectedSpaceIndex(Math.max(0, nextSpaces.findIndex((space) => space.id === created.activeSpaceId)));
|
|
572
590
|
setInputValue("");
|
|
573
591
|
setSetupStep("space");
|
|
592
|
+
setMessage("Space created. Confirm it to finish linking this device.");
|
|
574
593
|
}
|
|
575
594
|
catch (error) {
|
|
576
595
|
setMessage(`Could not create space: ${error.message}`);
|
|
@@ -597,6 +616,7 @@ function App() {
|
|
|
597
616
|
setSpaces(nextSpaces);
|
|
598
617
|
setSelectedSpaceIndex(0);
|
|
599
618
|
setSetupStep("space");
|
|
619
|
+
setMessage(`Loaded ${nextSpaces.length} space${nextSpaces.length === 1 ? "" : "s"} for ${company.name}.`);
|
|
600
620
|
}
|
|
601
621
|
catch (error) {
|
|
602
622
|
setMessage(`Could not load spaces: ${error.message}`);
|
|
@@ -626,6 +646,7 @@ function App() {
|
|
|
626
646
|
focus: setup.activeSpaceName,
|
|
627
647
|
status: "success",
|
|
628
648
|
});
|
|
649
|
+
setAuthSnapshot(setUmbrellaAuthAuthorized(serverUrl, setupEmail || null, companies.length));
|
|
629
650
|
setSetupStep("done");
|
|
630
651
|
setMessage(`Connected to ${setup.companyName} / ${setup.activeSpaceName}.`);
|
|
631
652
|
setActivePanel("home");
|
|
@@ -1214,7 +1235,7 @@ function App() {
|
|
|
1214
1235
|
const config = configManager.config;
|
|
1215
1236
|
let mainContent = null;
|
|
1216
1237
|
if (activePanel === "setup") {
|
|
1217
|
-
mainContent = (_jsx(SetupView, { busyLabel: busyLabel, companies: companies, config: config, inputValue: inputValue, message: message, onInputChange: setInputValue, selectedCompanyIndex: selectedCompanyIndex, selectedSpaceIndex: selectedSpaceIndex, serverUrl: serverUrl, spaces: spaces, step: setupStep }));
|
|
1238
|
+
mainContent = (_jsx(SetupView, { authSnapshot: authSnapshot, busyLabel: busyLabel, companies: companies, config: config, inputValue: inputValue, message: message, onInputChange: setInputValue, selectedCompanyIndex: selectedCompanyIndex, selectedSpaceIndex: selectedSpaceIndex, serverUrl: serverUrl, spaces: spaces, step: setupStep }));
|
|
1218
1239
|
}
|
|
1219
1240
|
else if (!config || !refreshState) {
|
|
1220
1241
|
mainContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "This device is not linked yet." }), _jsx(Text, { color: "gray", children: "Move to Setup and connect the repo to a company and space first." })] }));
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UmbrellaAuthSnapshot } from "./auth-state.js";
|
|
1
2
|
export interface AgentMemoryConfig {
|
|
2
3
|
umbrellaUrl?: string;
|
|
3
4
|
serverUrl: string;
|
|
@@ -25,6 +26,9 @@ export interface HubRegistry {
|
|
|
25
26
|
url: string;
|
|
26
27
|
updatedAt: string;
|
|
27
28
|
}
|
|
29
|
+
export interface StoredCliState {
|
|
30
|
+
authSnapshot?: UmbrellaAuthSnapshot;
|
|
31
|
+
}
|
|
28
32
|
export interface SavedLocation {
|
|
29
33
|
repoRoot: string;
|
|
30
34
|
companyId: string;
|
|
@@ -49,5 +53,8 @@ export declare class ConfigManager {
|
|
|
49
53
|
get hubRegistries(): HubRegistry[];
|
|
50
54
|
upsertHubRegistry(registry: HubRegistry): void;
|
|
51
55
|
removeHubRegistry(id: string): void;
|
|
56
|
+
get authSnapshot(): UmbrellaAuthSnapshot | null;
|
|
57
|
+
setAuthSnapshot(snapshot: UmbrellaAuthSnapshot): void;
|
|
58
|
+
clearAuthSnapshot(): void;
|
|
52
59
|
}
|
|
53
60
|
export declare const configManager: ConfigManager;
|
package/dist/config.js
CHANGED
|
@@ -128,5 +128,14 @@ export class ConfigManager {
|
|
|
128
128
|
const registries = this.hubRegistries.filter((entry) => entry.id !== id);
|
|
129
129
|
this.conf.set("hubRegistries", registries);
|
|
130
130
|
}
|
|
131
|
+
get authSnapshot() {
|
|
132
|
+
return this.conf.get("authSnapshot") ?? null;
|
|
133
|
+
}
|
|
134
|
+
setAuthSnapshot(snapshot) {
|
|
135
|
+
this.conf.set("authSnapshot", snapshot);
|
|
136
|
+
}
|
|
137
|
+
clearAuthSnapshot() {
|
|
138
|
+
this.conf.delete("authSnapshot");
|
|
139
|
+
}
|
|
131
140
|
}
|
|
132
141
|
export const configManager = new ConfigManager();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ cli.command("query [...args]", "Alias for search").action(async (args) => {
|
|
|
66
66
|
await searchCommandAction(args.join(" "));
|
|
67
67
|
});
|
|
68
68
|
cli.help();
|
|
69
|
-
cli.version("0.1.
|
|
69
|
+
cli.version("0.1.35");
|
|
70
70
|
const argv = process.argv.slice(2);
|
|
71
71
|
if (argv[0] === "curate" && argv[1] === "view" && argv.length === 2) {
|
|
72
72
|
await curateViewCommandAction();
|
package/dist/umbrella.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export type UmbrellaCliSetup = {
|
|
|
21
21
|
baseUrl: string;
|
|
22
22
|
apiKey: string;
|
|
23
23
|
};
|
|
24
|
+
type RequestFailureDetails = {
|
|
25
|
+
code?: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
status: number;
|
|
28
|
+
url: string;
|
|
29
|
+
};
|
|
30
|
+
export declare class UmbrellaRequestError extends Error {
|
|
31
|
+
status: number;
|
|
32
|
+
code?: string;
|
|
33
|
+
url: string;
|
|
34
|
+
constructor(details: RequestFailureDetails);
|
|
35
|
+
}
|
|
24
36
|
export declare function getUmbrellaHealth(serverUrl: string): Promise<DeploymentHealth>;
|
|
25
37
|
export declare function signInToUmbrella(serverUrl: string, email: string, password: string): Promise<{
|
|
26
38
|
cookie: string | null;
|
package/dist/umbrella.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export class UmbrellaRequestError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
url;
|
|
5
|
+
constructor(details) {
|
|
6
|
+
super(details.message ?? `Request failed (${details.status})`);
|
|
7
|
+
this.name = "UmbrellaRequestError";
|
|
8
|
+
this.status = details.status;
|
|
9
|
+
this.code = details.code;
|
|
10
|
+
this.url = details.url;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
1
13
|
function trimTrailingSlash(value) {
|
|
2
14
|
return value.replace(/\/+$/, "");
|
|
3
15
|
}
|
|
@@ -47,21 +59,38 @@ async function requestJson(url, options) {
|
|
|
47
59
|
.join("; ") || null;
|
|
48
60
|
const payload = await response.json().catch(() => null);
|
|
49
61
|
if (!response.ok) {
|
|
50
|
-
const
|
|
62
|
+
const details = payload && typeof payload === "object"
|
|
51
63
|
? (() => {
|
|
52
64
|
const record = payload;
|
|
53
65
|
const errorValue = record.error;
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
const codeValue = typeof record.code === "string" ? record.code : undefined;
|
|
67
|
+
const messageValue = typeof record.message === "string" ? record.message : undefined;
|
|
68
|
+
if (typeof errorValue === "string") {
|
|
69
|
+
return {
|
|
70
|
+
code: codeValue,
|
|
71
|
+
message: errorValue,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
56
74
|
if (errorValue && typeof errorValue === "object") {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
const nestedMessageValue = errorValue.message;
|
|
76
|
+
const nestedCodeValue = errorValue.code;
|
|
77
|
+
return {
|
|
78
|
+
code: typeof nestedCodeValue === "string" ? nestedCodeValue : codeValue,
|
|
79
|
+
message: typeof nestedMessageValue === "string" ? nestedMessageValue : messageValue,
|
|
80
|
+
};
|
|
60
81
|
}
|
|
61
|
-
return
|
|
82
|
+
return {
|
|
83
|
+
code: codeValue,
|
|
84
|
+
message: messageValue,
|
|
85
|
+
};
|
|
62
86
|
})()
|
|
63
|
-
:
|
|
64
|
-
throw new
|
|
87
|
+
: { code: undefined, message: undefined };
|
|
88
|
+
throw new UmbrellaRequestError({
|
|
89
|
+
code: details.code,
|
|
90
|
+
message: details.message ?? `Request failed (${response.status})`,
|
|
91
|
+
status: response.status,
|
|
92
|
+
url,
|
|
93
|
+
});
|
|
65
94
|
}
|
|
66
95
|
return {
|
|
67
96
|
data: payload,
|
package/package.json
CHANGED