diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..7a9dfa044d022f5ca8cf9191e37fef926dd67f77
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-chrome",
+            "request": "launch",
+            "name": "Launch Chrome against localhost",
+            "url": "http://localhost:8080",
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/src/app/computesite.ts b/src/app/computesite.ts
index aa2715fccc61c16380666978016bde0d432684cb..1ad0c3dc2c971ed9b5e75c4ebd2ab250a82135b2 100644
--- a/src/app/computesite.ts
+++ b/src/app/computesite.ts
@@ -12,7 +12,7 @@ export class Computesite {
   cafingerprint: string;  // Certificates contain a CA fingerprint. We use this
                           // to figure out which compute site a certificate is valid
                           // for
-  appCatalog: BehaviorSubject<Strudelapp[]>;
+  
   appCatalogUri: string;
   appCatalogCmd: string;
   internalfirewall: boolean; // Does a firewall exist within the site necessitating an extra level of ssh tunnel
@@ -23,18 +23,6 @@ export class Computesite {
   contact: string;
 }
 
-export class Strudelapp {
-  url: string; // A url used to retrieve extra config options. May be null
-  name: string; // Human readable name
-  startscript: string; // batch script ... should NOT include resource directives
-                       // resource directives like #SBATCH belong in the batchinterface
-  paramscmd: string;  // command to return extra data such as passwords and tokens
-                      // values returned here can be used in the client strings
-  client: {cmd: string[], redir: string};
-  localbind: boolean; // does the application bind to a port on the localhost
-                      // interface or on all interfaces. This behaviour determins
-                      /// how we create tunnels
-}
 
 export class Health {
   stat: string;
diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts
index 42b049926055d3dcf7bb3e3088e258d0c440a0f3..8ef1ab93ad2017fab06a90655024a291eff5c584 100644
--- a/src/app/computesites.service.ts
+++ b/src/app/computesites.service.ts
@@ -1,5 +1,6 @@
 import { Injectable } from '@angular/core';
-import { Computesite, Strudelapp } from './computesite';
+import { Computesite } from './computesite';
+import { Strudelapp } from './strudelapp';
 import {BehaviorSubject, of, combineLatest} from 'rxjs';
 import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
 import { Observable } from 'rxjs';
@@ -46,7 +47,7 @@ export class ComputesitesService {
       let options = { headers: headers, withCredentials: false};
       if (s.appCatalogUri !== null) {
         this.http.get<Strudelapp[]>(s.appCatalogUri,options)
-                         .pipe(catchError(this.handleError('getStrudelApps')))
+                         .pipe(catchError(this.handleError('  s')))
                          .subscribe(resp => this.updateStrudelApps(s,resp));
       }
     }
@@ -64,7 +65,7 @@ export class ComputesitesService {
     if (ids != undefined && ids != null) {
       for (let id of ids) {
         this.getStrudelAppsIdentity(id)
-                           .subscribe(resp => this.updateStrudelApps(id.site,resp));
+                           .subscribe(resp => this.updateStrudelApps(id,resp));
       }
     }
   }
@@ -81,9 +82,12 @@ export class ComputesitesService {
   }
 
   private getStrudelAppsCmd(identity: Identity): Observable<Strudelapp[]> {
+    console.log('running getStrudleAppsCmd',identity)
     if (identity.site.appCatalogCmd != null) {
       return (this.tesService.runCommand(identity,identity.site.appCatalogCmd) as Observable<Strudelapp[]>)
-                    .pipe(catchError(this.handleError('getStrudelApps', <Strudelapp[]>[])))
+                    .pipe(
+                      tap((v) => console.log('getapps',identity,' returned',v)),
+                      catchError(this.handleError('getStrudelApps', <Strudelapp[]>[])))
     }
     return of([] as Strudelapp[])
   }
@@ -107,18 +111,18 @@ export class ComputesitesService {
   }
 
 
-  updateStrudelApps(site: Computesite, apps) {
+  updateStrudelApps(id: Identity, apps) {
     var sapps: Strudelapp[];
     var localapps: Strudelapp[];
     sapps = <Strudelapp[]>apps;
 
-    localapps = JSON.parse(localStorage.getItem(site.name+'-apps'))
+    localapps = JSON.parse(localStorage.getItem(id.site.name+'-apps'))
     if (localapps !== null)  {
       for (let a of localapps) {
         sapps.push(a);
       }
     }
-    site.appCatalog.next(sapps);
+    id.appCatalog.next(sapps);
   }
 
   private handleError<T> (operation = 'operation', result?: T) {
@@ -160,13 +164,11 @@ export class ComputesitesService {
     }
     if (localcomputesites !== null ) {
       for (let cs of localcomputesites) {
-        cs.appCatalog  = new BehaviorSubject<Strudelapp[]>([]);
         computesites.push(cs);
       }
     }
     for (let cs of resp) {
       let computesite = <Computesite>cs;
-      computesite.appCatalog = new BehaviorSubject<Strudelapp[]>([])
       computesites.push(computesite);
     }
     this.computesites.next(computesites);
diff --git a/src/app/identity.ts b/src/app/identity.ts
index ac8610079b5f224d3e3b00c2447eb178228eca8b..a332f822790418ba53e62282224fa455e1d12e72 100644
--- a/src/app/identity.ts
+++ b/src/app/identity.ts
@@ -1,6 +1,7 @@
 import {Computesite, Health} from './computesite';
 import {Job} from './job';
 import {BehaviorSubject} from 'rxjs';
+import {Strudelapp} from './strudelapp';
 // Identities are defined by a username on a computer, but rather than just
 // DNS entry, there is extra info in the Computesite
 export class Identity {
@@ -13,6 +14,7 @@ export class Identity {
   joblist: BehaviorSubject<Job[]>;
   quotas: any[];
   expiry: number;
+  appCatalog: BehaviorSubject<Strudelapp[]>;
   constructor( username: string, site: Computesite, expiry: number) {
     this.username = username;
     this.site = site;
@@ -20,8 +22,10 @@ export class Identity {
     this.systemalerts = new BehaviorSubject<Health[]>(null);
     this.accountalerts = new BehaviorSubject<Health[]>(null);
     this.joblist = new BehaviorSubject<Job[]>([]);
+    this.appCatalog = new BehaviorSubject<Strudelapp[]>([]);
     this.quotas = [];
     this.expiry = expiry;
+    
   }
 
   copy_skip_catalog(): Identity {
diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts
index e336904dbfc282aba3d18af23aa060dc649e3a0f..7aa175c87108bf736664418106bc246a7ef1f118 100644
--- a/src/app/joblist/joblist.component.ts
+++ b/src/app/joblist/joblist.component.ts
@@ -92,7 +92,7 @@ export class JoblistComponent implements OnInit {
    // any jobs in the joblist that we don't know which application they arem try to figure it out
    for (j of joblist) {
      if (j.app === undefined || j.app == null) {
-       j.app = Strudelapp.getApp(j.appname,identity.site.appCatalog.value);
+       j.app = Strudelapp.getApp(j.appname,identity.appCatalog.value);
      }
      if (j.identity == undefined) {
         j.identity = identity;
diff --git a/src/app/jobs.service.ts b/src/app/jobs.service.ts
index b244cacc0a2b96da636675baf14ce394ffa5df7c..a20b8026dcda9bb537d54604a08b6004dc19617c 100644
--- a/src/app/jobs.service.ts
+++ b/src/app/jobs.service.ts
@@ -45,13 +45,15 @@ export class JobsService {
     if (error.hasOwnProperty("error") && error.error.hasOwnProperty("message")) {
       if (error.error.message.indexOf("Permission denied") != -1) {
         this.notifications.notify("Your login appears to have expired. Please log in again");
-        this.authService.updateAgentContents().subscribe((_) => {return});
+        //this.authService.updateAgentContents().subscribe((_) => {return});
         return;
       } 
+      this.notifications.notify("Unable to retrieve a list of running jobs.\nDid your session expire?\nThe error messge was " + error.error.message);
+      return;
     }
-    this.notifications.notify("Unable to retrieve a list of running jobs.\nDid your session expire?")
     console.error(error);
-    this.authService.updateAgentContents().subscribe((_) => {return});
+    this.notifications.notify("Unable to retrieve a list of running jobs.\nDid your session expire?");
+    //this.authService.updateAgentContents().subscribe((_) => {return});
   }
 
 }
diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html
index d71b9a883ee9b4462ed73326ff6ff6cc6b6d47a9..1211c94bf54b6f49ba2a15f148533fd5dd8b9a55 100644
--- a/src/app/launcher/launcher.component.html
+++ b/src/app/launcher/launcher.component.html
@@ -37,7 +37,7 @@
                                 </button>
                         </mat-list-item>
                         
-                        <app-strudelapplist [applist]=id.site.appCatalog [identity]="id" (appChange)="selectApp($event)" style="width: 100%"></app-strudelapplist>
+                        <app-strudelapplist [applist]=id.appCatalog [identity]="id" (appChange)="selectApp($event)" style="width: 100%"></app-strudelapplist>
                         </mat-list>
                     </mat-expansion-panel>
                     <!--<div style="width: 100%; border-bottom: 1px solid var(--panel-border-color);"></div>-->
diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts
index f9770a80c20f98a1b569929e539dea129ab1b17d..f5d037fd3f7af591c8e7dea4e04ad387cdb06b39 100644
--- a/src/app/launcher/launcher.component.ts
+++ b/src/app/launcher/launcher.component.ts
@@ -233,8 +233,8 @@ export class LauncherComponent implements OnInit {
     if (apps === null) {
       apps = 'accountinfo';
     }
-    if (id.site.appCatalog.value === undefined || id.site.appCatalog.value === null || (id.site.appCatalog.value).length == 0) {
-      id.site.appCatalog.pipe(filter((v) => v !== null && v.length > 0),take(1)).subscribe(() => this.updateIdApp(params,appidentities));
+    if (id.appCatalog.value === undefined || id.appCatalog.value === null || (id.appCatalog.value).length == 0) {
+      id.appCatalog.pipe(filter((v) => v !== null && v.length > 0),take(1)).subscribe(() => this.updateIdApp(params,appidentities));
       return
     }
     var app: Strudelapp = this.getApp(id,apps);
@@ -247,7 +247,7 @@ export class LauncherComponent implements OnInit {
     if (id == 'accountinfo') {
       return null;
     }
-    return Strudelapp.getApp(v,id.site.appCatalog.value);
+    return Strudelapp.getApp(v,id.appCatalog.value);
   }
 
   getId(v: any, appidentities: Identity[]) {
diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts
index 9d5857b1390cc56a577e9dacf919a72c82728860..4aaaef5f6a34b1fefa54d6cc8197c0bb7cb2a8d1 100644
--- a/src/app/tes.service.ts
+++ b/src/app/tes.service.ts
@@ -54,7 +54,6 @@ public openWindow$: Subject<any>;
  private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appinst?: any): string {
    let params = new URLSearchParams();
    let id = identity.copy_skip_catalog();
-   id.site.appCatalog = null;
    if (appinst!== null)  {
      params.set('appinstance',JSON.stringify(appinst));
    }
diff --git a/src/assets/config/m3apps.test.json b/src/assets/config/m3apps.test.json
index b12b6fa77e46aa3a9c0db500aeac691da2da22a3..bcac520e069c97b25134fa3a13e169333b4718d3 100644
--- a/src/assets/config/m3apps.test.json
+++ b/src/assets/config/m3apps.test.json
@@ -43,7 +43,7 @@
    "instactions": [ 
        {
            "name": "Connect",
-           "paramscmd": "/usr/local/sv2/jupyter/jupyter_params.sh {jobid}",
+           "paramscmd": "/usr/local/sv2/jupyter/jupyter_params.py {jobid}",
            "client": {"cmd": null, "redir": "?token={token}"},
            "states": ["RUNNING"]
        },