mop-agent 0.1.6 → 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 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.6` contains the corrected VPS
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({ ownerExists: ownerExists(), authenticated: !!session });
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;
@@ -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
- fetch("/api/setup/status")
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("/assistant");
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
- // Better Auth creates the first session together with the owner account.
42
- window.location.replace("/assistant");
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
- window.location.replace("/assistant");
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() });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "mop-agent",
3
- "version": "0.1.6",
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.6",
9
+ "version": "0.1.7",
10
10
  "license": "UNLICENSED",
11
11
  "workspaces": [
12
12
  "packages/*",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mop-agent",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Self-hosted AI assistant with persistent cross-project memory, installed with npx mop-agent.",
5
5
  "author": "BURHANDEV ENTERPRISE",
6
6
  "license": "UNLICENSED",