mop-agent 0.1.5 → 0.1.7
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 +1 -1
- package/apps/web/app/api/setup/status/route.ts +9 -1
- package/apps/web/app/assistant/layout.tsx +3 -0
- package/apps/web/app/brain/layout.tsx +3 -0
- package/apps/web/app/chat/layout.tsx +3 -0
- package/apps/web/app/globals.css +88 -1
- package/apps/web/app/page.tsx +3 -0
- package/apps/web/app/settings/layout.tsx +3 -0
- package/apps/web/app/setup/page.tsx +52 -6
- package/apps/web/app/team/layout.tsx +3 -0
- package/apps/web/lib/page-auth.ts +3 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ through MOP-FLOW. It stores project memory, performs semantic recall and
|
|
|
5
5
|
consolidation, serves grounded chat, and can request approved actions from a
|
|
6
6
|
linked FLOW node.
|
|
7
7
|
|
|
8
|
-
> **Release status:** npm package `mop-agent@0.1.
|
|
8
|
+
> **Release status:** npm package `mop-agent@0.1.7` contains the corrected VPS
|
|
9
9
|
> installer and first-run Admin/Assistant flow. The canonical installation command is
|
|
10
10
|
> exactly `npx mop-agent`.
|
|
11
11
|
|
|
@@ -3,5 +3,13 @@ import { auth, ownerExists } from "@/lib/auth";
|
|
|
3
3
|
|
|
4
4
|
export async function GET(req: Request): Promise<Response> {
|
|
5
5
|
const session = await auth.api.getSession({ headers: req.headers });
|
|
6
|
-
return Response.json(
|
|
6
|
+
return Response.json(
|
|
7
|
+
{ ownerExists: ownerExists(), authenticated: !!session },
|
|
8
|
+
{
|
|
9
|
+
headers: {
|
|
10
|
+
"Cache-Control": "private, no-store, no-cache, max-age=0, must-revalidate",
|
|
11
|
+
"Vary": "Cookie",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
);
|
|
7
15
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { requirePageSession } from "@/lib/page-auth";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
export default async function AssistantLayout({ children }: { children: ReactNode }) {
|
|
5
8
|
await requirePageSession();
|
|
6
9
|
return children;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { requirePageSession } from "@/lib/page-auth";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
export default async function BrainLayout({ children }: { children: ReactNode }) {
|
|
5
8
|
await requirePageSession();
|
|
6
9
|
return children;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { requirePageSession } from "@/lib/page-auth";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
export default async function ChatLayout({ children }: { children: ReactNode }) {
|
|
5
8
|
await requirePageSession();
|
|
6
9
|
return children;
|
package/apps/web/app/globals.css
CHANGED
|
@@ -8,9 +8,96 @@
|
|
|
8
8
|
|
|
9
9
|
* { box-sizing: border-box; }
|
|
10
10
|
html { color-scheme: light; }
|
|
11
|
-
body {
|
|
11
|
+
body {
|
|
12
|
+
min-height: 100vh;
|
|
13
|
+
position: relative;
|
|
14
|
+
background-color: var(--mop-cream);
|
|
15
|
+
background-image:
|
|
16
|
+
linear-gradient(rgba(45, 74, 62, .055) 1px, transparent 1px),
|
|
17
|
+
linear-gradient(90deg, rgba(116, 34, 32, .04) 1px, transparent 1px);
|
|
18
|
+
background-size: 8px 8px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Global CRT/pixel veil: fixed above every route, but never catches input. */
|
|
22
|
+
body::before {
|
|
23
|
+
content: "";
|
|
24
|
+
position: fixed;
|
|
25
|
+
inset: 0;
|
|
26
|
+
z-index: 2147483646;
|
|
27
|
+
pointer-events: none;
|
|
28
|
+
background-image:
|
|
29
|
+
repeating-linear-gradient(
|
|
30
|
+
0deg,
|
|
31
|
+
rgba(45, 74, 62, .035) 0,
|
|
32
|
+
rgba(45, 74, 62, .035) 1px,
|
|
33
|
+
transparent 1px,
|
|
34
|
+
transparent 4px
|
|
35
|
+
),
|
|
36
|
+
repeating-linear-gradient(
|
|
37
|
+
90deg,
|
|
38
|
+
rgba(116, 34, 32, .022) 0,
|
|
39
|
+
rgba(116, 34, 32, .022) 1px,
|
|
40
|
+
transparent 1px,
|
|
41
|
+
transparent 4px
|
|
42
|
+
);
|
|
43
|
+
background-size: 4px 4px;
|
|
44
|
+
mix-blend-mode: multiply;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
body::after {
|
|
48
|
+
content: "";
|
|
49
|
+
position: fixed;
|
|
50
|
+
inset: 0;
|
|
51
|
+
z-index: 2147483647;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
background:
|
|
54
|
+
radial-gradient(circle at center, transparent 56%, rgba(45, 74, 62, .08) 100%),
|
|
55
|
+
repeating-linear-gradient(0deg, transparent 0, transparent 7px, rgba(116, 34, 32, .025) 7px, rgba(116, 34, 32, .025) 8px);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
button,
|
|
59
|
+
input,
|
|
60
|
+
select,
|
|
61
|
+
textarea {
|
|
62
|
+
border-radius: 3px !important;
|
|
63
|
+
box-shadow: 2px 2px 0 rgba(45, 74, 62, .17);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
button {
|
|
67
|
+
transition: transform 80ms steps(2, end), box-shadow 80ms steps(2, end);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
button:hover:not(:disabled) {
|
|
71
|
+
transform: translate(-1px, -1px);
|
|
72
|
+
box-shadow: 3px 3px 0 rgba(45, 74, 62, .24);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
button:active:not(:disabled) {
|
|
76
|
+
transform: translate(1px, 1px);
|
|
77
|
+
box-shadow: 0 0 0 rgba(45, 74, 62, 0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
code,
|
|
81
|
+
pre,
|
|
82
|
+
input,
|
|
83
|
+
textarea {
|
|
84
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
h1,
|
|
88
|
+
h2,
|
|
89
|
+
h3,
|
|
90
|
+
button {
|
|
91
|
+
letter-spacing: .025em;
|
|
92
|
+
}
|
|
93
|
+
|
|
12
94
|
::selection { background: #742220; color: #fef9e1; }
|
|
13
95
|
|
|
96
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
97
|
+
body::before,
|
|
98
|
+
body::after { display: none; }
|
|
99
|
+
}
|
|
100
|
+
|
|
14
101
|
@media (max-width: 760px) {
|
|
15
102
|
.mop-setup-shell { grid-template-columns: 1fr !important; }
|
|
16
103
|
.mop-setup-brand { min-height: 34vh; padding: 36px !important; }
|
package/apps/web/app/page.tsx
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { requirePageSession } from "@/lib/page-auth";
|
|
2
2
|
import { redirect } from "next/navigation";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
/**
|
|
5
8
|
* Product entry point. First-run and signed-out users belong in the account
|
|
6
9
|
* setup flow; authenticated users land in the assistant, not the Brain tools.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { requirePageSession } from "@/lib/page-auth";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
export default async function SettingsLayout({ children }: { children: ReactNode }) {
|
|
5
8
|
await requirePageSession();
|
|
6
9
|
return children;
|
|
@@ -3,6 +3,30 @@
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { signIn, signUp } from "@/lib/auth-client";
|
|
5
5
|
|
|
6
|
+
type SetupStatus = { ownerExists: boolean; authenticated: boolean };
|
|
7
|
+
|
|
8
|
+
async function getSetupStatus(): Promise<SetupStatus> {
|
|
9
|
+
const response = await fetch(`/api/setup/status?t=${Date.now()}`, {
|
|
10
|
+
cache: "no-store",
|
|
11
|
+
credentials: "include",
|
|
12
|
+
headers: { "cache-control": "no-cache" },
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) throw new Error(`setup status ${response.status}`);
|
|
15
|
+
return response.json() as Promise<SetupStatus>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function waitForSession(): Promise<boolean> {
|
|
19
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
20
|
+
try {
|
|
21
|
+
if ((await getSetupStatus()).authenticated) return true;
|
|
22
|
+
} catch {
|
|
23
|
+
// A short deployment/network gap should not strand the form in busy mode.
|
|
24
|
+
}
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
6
30
|
export default function SetupPage() {
|
|
7
31
|
const [ownerExists, setOwnerExists] = useState<boolean | null>(null);
|
|
8
32
|
const [email, setEmail] = useState("");
|
|
@@ -13,11 +37,10 @@ export default function SetupPage() {
|
|
|
13
37
|
const [mode, setMode] = useState<"signup" | "signin">("signup");
|
|
14
38
|
|
|
15
39
|
useEffect(() => {
|
|
16
|
-
|
|
17
|
-
.then((r) => r.json() as Promise<{ ownerExists: boolean; authenticated: boolean }>)
|
|
40
|
+
getSetupStatus()
|
|
18
41
|
.then((status) => {
|
|
19
42
|
if (status.authenticated) {
|
|
20
|
-
window.location.replace(
|
|
43
|
+
window.location.replace(`/assistant?auth=${Date.now()}`);
|
|
21
44
|
return;
|
|
22
45
|
}
|
|
23
46
|
setOwnerExists(status.ownerExists);
|
|
@@ -38,8 +61,26 @@ export default function SetupPage() {
|
|
|
38
61
|
setBusy(false);
|
|
39
62
|
return;
|
|
40
63
|
}
|
|
41
|
-
//
|
|
42
|
-
|
|
64
|
+
// Verify the cookie before entering a server-protected route. If the
|
|
65
|
+
// signup response did not establish it, explicitly sign in once.
|
|
66
|
+
if (!(await waitForSession())) {
|
|
67
|
+
const login = await signIn.email({ email, password });
|
|
68
|
+
if (login.error) {
|
|
69
|
+
setMode("signin");
|
|
70
|
+
setOwnerExists(true);
|
|
71
|
+
setMsg(login.error.message ?? "Admin created, but sign in failed. Please sign in below.");
|
|
72
|
+
setBusy(false);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (await waitForSession()) {
|
|
77
|
+
window.location.replace(`/assistant?auth=${Date.now()}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setMode("signin");
|
|
81
|
+
setOwnerExists(true);
|
|
82
|
+
setMsg("Admin created, but the session cookie was not accepted. Please sign in again.");
|
|
83
|
+
setBusy(false);
|
|
43
84
|
return;
|
|
44
85
|
}
|
|
45
86
|
|
|
@@ -49,7 +90,12 @@ export default function SetupPage() {
|
|
|
49
90
|
setBusy(false);
|
|
50
91
|
return;
|
|
51
92
|
}
|
|
52
|
-
|
|
93
|
+
if (await waitForSession()) {
|
|
94
|
+
window.location.replace(`/assistant?auth=${Date.now()}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setMsg("Sign in succeeded, but the session cookie was not accepted. Check that you are using the configured HTTPS domain.");
|
|
98
|
+
setBusy(false);
|
|
53
99
|
}
|
|
54
100
|
|
|
55
101
|
const loading = ownerExists === null && !msg;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { requirePageSession } from "@/lib/page-auth";
|
|
3
3
|
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
export const revalidate = 0;
|
|
6
|
+
|
|
4
7
|
export default async function TeamLayout({ children }: { children: ReactNode }) {
|
|
5
8
|
await requirePageSession();
|
|
6
9
|
return children;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { auth, ownerExists } from "@/lib/auth";
|
|
2
2
|
import { headers } from "next/headers";
|
|
3
3
|
import { redirect } from "next/navigation";
|
|
4
|
+
import { unstable_noStore as noStore } from "next/cache";
|
|
4
5
|
|
|
5
6
|
/** Server-side guard for authenticated application pages. */
|
|
6
7
|
export async function requirePageSession() {
|
|
8
|
+
// Auth redirects are cookie-specific and must never enter Next's route cache.
|
|
9
|
+
noStore();
|
|
7
10
|
if (!ownerExists()) redirect("/setup");
|
|
8
11
|
|
|
9
12
|
const session = await auth.api.getSession({ headers: await headers() });
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mop-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "mop-agent",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.7",
|
|
10
10
|
"license": "UNLICENSED",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"packages/*",
|
package/package.json
CHANGED